Rust std::fmt formating parameters. Is [fill] from variable possible? [duplicate] - rust

With Rust I can have a user-specified width in my call to format!,
format!("{:>width$}", result.clone(), width=v as usize )
I can even specify the fill character (like the - below)
format!("{:->width$}", result.clone(), width=v as usize )
Or, with a 0,
format!("{:0>width$}", result.clone(), width=v as usize )
But is there a way to have that user-specified? I've tried the following but it doesn't work,
format!("{:fill$>width$}", result.clone(), fill='0' width=v as usize )
I get the following error,
error: invalid format string: expected `'}'`, found `'>'`
--> src/sequence/renderer.rs:124:28
|
124 | |v| Ok(format!("{:fill$>width$}", result.clone(), fill='0', width=v as usize ))
| - ^ expected `}` in format string
| |
| because of this opening brace
|
= note: if you intended to print `{`, you can escape it using `{{`
error: could not compile `letter-sequence` due to previous error
warning: build failed, waiting for other jobs to finish...
error: build failed

It's not possible at the moment. By looking at the grammar of format string it's not possible to pass fill parameter as an argument.
format := '{' [ argument ] [ ':' format_spec ] '}'
argument := integer | identifier
format_spec := [[fill]align][sign]['#']['0'][width]['.' precision]type
fill := character
align := ''
sign := '+' | '-'
width := count
precision := count | '*'
type := '' | '?' | 'x?' | 'X?' | identifier
count := parameter | integer
parameter := argument '$'
As you can see the fill format specifier is directly mapped to a character token, Where as width can be substituted with identifier $(width -> count -> parameter -> argument $ -> identifier $)

You can use runtime-fmt (https://crates.io/crates/runtime-fmt). The idea is to do something like this
!!! not tested !!!
#[macro_use] extern crate runtime_fmt;
fn main()
{
let result = 400;
let v = 45;
let f = format!("{{:{file}>{width}}}", width=v as usize, file='0');
let s = rt_format!(f, result).unwrap();
println!("{}", s);
}
Some interesting links:
https://github.com/rust-lang/rfcs/issues/543
https://fmt.dev/latest/syntax.html

Related

Does the rust format! macro provide for user-specified fill characters

With Rust I can have a user-specified width in my call to format!,
format!("{:>width$}", result.clone(), width=v as usize )
I can even specify the fill character (like the - below)
format!("{:->width$}", result.clone(), width=v as usize )
Or, with a 0,
format!("{:0>width$}", result.clone(), width=v as usize )
But is there a way to have that user-specified? I've tried the following but it doesn't work,
format!("{:fill$>width$}", result.clone(), fill='0' width=v as usize )
I get the following error,
error: invalid format string: expected `'}'`, found `'>'`
--> src/sequence/renderer.rs:124:28
|
124 | |v| Ok(format!("{:fill$>width$}", result.clone(), fill='0', width=v as usize ))
| - ^ expected `}` in format string
| |
| because of this opening brace
|
= note: if you intended to print `{`, you can escape it using `{{`
error: could not compile `letter-sequence` due to previous error
warning: build failed, waiting for other jobs to finish...
error: build failed
It's not possible at the moment. By looking at the grammar of format string it's not possible to pass fill parameter as an argument.
format := '{' [ argument ] [ ':' format_spec ] '}'
argument := integer | identifier
format_spec := [[fill]align][sign]['#']['0'][width]['.' precision]type
fill := character
align := ''
sign := '+' | '-'
width := count
precision := count | '*'
type := '' | '?' | 'x?' | 'X?' | identifier
count := parameter | integer
parameter := argument '$'
As you can see the fill format specifier is directly mapped to a character token, Where as width can be substituted with identifier $(width -> count -> parameter -> argument $ -> identifier $)
You can use runtime-fmt (https://crates.io/crates/runtime-fmt). The idea is to do something like this
!!! not tested !!!
#[macro_use] extern crate runtime_fmt;
fn main()
{
let result = 400;
let v = 45;
let f = format!("{{:{file}>{width}}}", width=v as usize, file='0');
let s = rt_format!(f, result).unwrap();
println!("{}", s);
}
Some interesting links:
https://github.com/rust-lang/rfcs/issues/543
https://fmt.dev/latest/syntax.html

Is there syntax for declaring character literals in hexadecimal notation?

Something like
const X: char = '0x10FFFC';
Yes, use \u{..}:
const X: char = '\u{10FFFC}';
Playground
One trick for this cases is play with the compiler. If you try the following code it will give you a nice hint about what to do for example:
const X: char = 0x10FFFC as char;
error: only `u8` can be cast into `char`
--> src/lib.rs:1:17
|
1 | const X: char = 0x10FFFC as char;
| ^^^^^^^^^^^^^^^^ help: use a `char` literal instead: `'\u{10FFFC}'`
|
= note: `#[deny(overflowing_literals)]` on by default

Proper bounds on associated types to allow default methods on traits [duplicate]

I wanted to implement a function computing the number of digits within any generic type of integer. Here is the code I came up with:
extern crate num;
use num::Integer;
fn int_length<T: Integer>(mut x: T) -> u8 {
if x == 0 {
return 1;
}
let mut length = 0u8;
if x < 0 {
length += 1;
x = -x;
}
while x > 0 {
x /= 10;
length += 1;
}
length
}
fn main() {
println!("{}", int_length(45));
println!("{}", int_length(-45));
}
And here is the compiler output
error[E0308]: mismatched types
--> src/main.rs:5:13
|
5 | if x == 0 {
| ^ expected type parameter, found integral variable
|
= note: expected type `T`
found type `{integer}`
error[E0308]: mismatched types
--> src/main.rs:10:12
|
10 | if x < 0 {
| ^ expected type parameter, found integral variable
|
= note: expected type `T`
found type `{integer}`
error: cannot apply unary operator `-` to type `T`
--> src/main.rs:12:13
|
12 | x = -x;
| ^^
error[E0308]: mismatched types
--> src/main.rs:15:15
|
15 | while x > 0 {
| ^ expected type parameter, found integral variable
|
= note: expected type `T`
found type `{integer}`
error[E0368]: binary assignment operation `/=` cannot be applied to type `T`
--> src/main.rs:16:9
|
16 | x /= 10;
| ^ cannot use `/=` on type `T`
I understand that the problem comes from my use of constants within the function, but I don't understand why the trait specification as Integer doesn't solve this.
The documentation for Integer says it implements the PartialOrd, etc. traits with Self (which I assume refers to Integer). By using integer constants which also implement the Integer trait, aren't the operations defined, and shouldn't the compiler compile without errors?
I tried suffixing my constants with i32, but the error message is the same, replacing _ with i32.
Many things are going wrong here:
As Shepmaster says, 0 and 1 cannot be converted to everything implementing Integer. Use Zero::zero and One::one instead.
10 can definitely not be converted to anything implementing Integer, you need to use NumCast for that
a /= b is not sugar for a = a / b but an separate trait that Integer does not require.
-x is an unary operation which is not part of Integer but requires the Neg trait (since it only makes sense for signed types).
Here's an implementation. Note that you need a bound on Neg, to make sure that it results in the same type as T
extern crate num;
use num::{Integer, NumCast};
use std::ops::Neg;
fn int_length<T>(mut x: T) -> u8
where
T: Integer + Neg<Output = T> + NumCast,
{
if x == T::zero() {
return 1;
}
let mut length = 0;
if x < T::zero() {
length += 1;
x = -x;
}
while x > T::zero() {
x = x / NumCast::from(10).unwrap();
length += 1;
}
length
}
fn main() {
println!("{}", int_length(45));
println!("{}", int_length(-45));
}
The problem is that the Integer trait can be implemented by anything. For example, you could choose to implement it on your own struct! There wouldn't be a way to convert the literal 0 or 1 to your struct. I'm too lazy to show an example of implementing it, because there's 10 or so methods. ^_^
num::Zero and num::One
This is why Zero::zero and One::one exist. You can (very annoyingly) create all the other constants from repeated calls to those.
use num::{One, Zero}; // 0.4.0
fn three<T>() -> T
where
T: Zero + One,
{
let mut three = Zero::zero();
for _ in 0..3 {
three = three + One::one();
}
three
}
From and Into
You can also use the From and Into traits to convert to your generic type:
use num::Integer; // 0.4.0
use std::ops::{DivAssign, Neg};
fn int_length<T>(mut x: T) -> u8
where
T: Integer + Neg<Output = T> + DivAssign,
u8: Into<T>,
{
let zero = 0.into();
if x == zero {
return 1;
}
let mut length = 0u8;
if x < zero {
length += 1;
x = -x;
}
while x > zero {
x /= 10.into();
length += 1;
}
length
}
fn main() {
println!("{}", int_length(45));
println!("{}", int_length(-45));
}
See also:
How do I use floating point number literals when using generic types?

What does the '#' symbol do in Rust?

I forgot to specify the type of a parameter and the error message was as follows:
error: expected one of `:` or `#`, found `)`
--> src/main.rs:2:12
|
2 | fn func(arg)
| ^ expected one of `:` or `#` here
Which raises the question: what can you do with an # symbol? I don't remember reading about using the # symbol for anything. I also did some Googling and couldn't find anything. What does # do?
You can use the # symbol to bind a pattern to a name. As the Rust Reference demonstrates:
let x = 1;
match x {
e # 1 ... 5 => println!("got a range element {}", e),
_ => println!("anything"),
}
Assignments in Rust allow pattern expressions (provided they are complete) and argument lists are no exception. In the specific case of #, this isn't very useful because you can already name the matched parameter. However, for completeness, here is an example which compiles:
enum MyEnum {
TheOnlyCase(u8),
}
fn my_fn(x # MyEnum::TheOnlyCase(_): MyEnum) {}

Returning a value from within an if statement has a "mismatched types" error

In the function below, I match the first full character of a &str, and if it is a *, -, or _ and if it is those that character is returned, and with the _ arm I want to check if the character is whitespace, and return 'a' otherwise.
fn print_character(text: &str) {
let character: char = match text.chars().nth(0).unwrap() {
ch # '*' | ch # '-' | ch # '_' => ch,
ch # _ => {
if !ch.is_whitespace() {
return 'a';
}
' '
}
};
println!("{}", character);
}
When I run the code I get the error below:
error[E0308]: mismatched types
--> src/main.rs:6:24
|
6 | return 'a';
| ^^^ expected (), found char
|
= note: expected type `()`
found type `char`
You don't want a return here, you're not trying to return from the function. Just use the 'a' as an expression. You also need the space char as an else branch, not standing on its own.
if !ch.is_whitespace() {
'a'
} else {
' '
}
Why the else is required
if is an expression, and it has to evaluate to some value. That value needs a definite type; it can't sometimes be a char and sometimes something else. If you were to just do this:
if !ch.is_whitespace() {
'a'
}
What would the if expression evaluate to in case the test fails? Instead of just evaluating to some arbitrary char value, the language simply requires an else branch. If you don't want to use the if as an expression, and just use it for its side-effects, then you can leave out the else. In that case, it is still an expression, but its value is () (irrespective of whether the test passed or not), and you need to end it with a statement.

Resources