Use write! macro with a string instead of a string literal - rust

I've written the following function:
fn print_error(text: &str) {
let mut t = term::stdout().unwrap();
t.fg(term::color::RED).unwrap();
(write!(t, text)).unwrap();
assert!(t.reset().unwrap());
}
It should take the string and print it out on the console in red.
When I try to to compile, the compiler says:
error: format argument must be a string literal.
--> src/main.rs:4:16
|
4 | (write!(t, text)).unwrap();
| ^^^^
After a lot of searching, I've found out that I'm able to replace the text variable with e.g. "text" and it will work because it's a string literal, which the write! macro needs.
How could I use the write! macro with a string instead of a string literal? Or is there a better way to colourize the terminal output?

Just use write!(t, "{}", text).
I think you're missing the thrust of the error message. write! has two mandatory arguments:
A location to write to.
A format string.
The second parameter is not just any arbitrary string, it's the format string.
See also:
println! error: expected a literal / format argument must be a string literal

Related

include_bytes macro won't take argument, even if it is of type &'static str

I was using glium, and tried to make a struct to make using textures easier. Problem is, the function I am using to initialize the struct takes an &'static str as an argument, but the include_bytes!() macro won't take it, and the compiler is saying that the argument must be a string literal.
Here's the dysfunctional code:
fn load(path : String, format : ImageFormat, display : glium::Display) -> Image{
let image = image::load(Cursor::new(&include_bytes!(path)[..]),
format).unwrap().to_rgba8();
let image_dimensions = image.dimensions();
let image = glium::texture::RawImage2d::from_raw_rgba_reversed(&image.into_raw(), image_dimensions);
let _opengl_texture = glium::texture::CompressedSrgbTexture2d::new(&display, image).unwrap();
return Image{opengl_texture : _opengl_texture}
}
I tried using String and converting it in multiple ways, but it still didn't work.
The include_bytes! macro is for loading data from a file at compile-time and embedding it in the executable. Therefore the path must be known at compile-time via a string literal. Your path variable is only known at run-time.
Perhaps you wanted std::fs::read instead?

Why can't print a struct field with formatted print?

Like following code,
it does working to print values with formatted print, when values are primitive values or struct(with derive debug attribute), or something.
But I can't print values when values are struct fields.
#[derive(Debug)]
struct Point<T> {
x: T,
y: T,
}
fn main() {
let a = 3;
let p = Point { x: 5, y: 10 };
println!("{}", a); // Working
println!("{a}"); // Working
println!("{:?}", p); // Working
println!("{p:?}"); // Working
println!("{} {}", p.x, p.y); // Working
println!("{p.x} {p.y}"); // Not working
}
The error message is following.
error: invalid format string: expected `'}'`, found `'.'`
--> src/main.rs:18:17
|
18 | println!("{p.x} {p.y}"); // Not working
| - ^ expected `}` in format string
| |
| because of this opening brace
|
= note: if you intended to print `{`, you can escape it using `{{`
error: could not compile `rust-ex` due to previous error
I think that the type of p.x and p.y is i32 so they can printed with formatted print but it doesn't.
Is there any way to print struct fields with formatter print? Or is there anything need to implement?
Your problem is not a formatting problem, just an issue with what exactly println! accepts in its formatting string.
Contrary to what you may experience in other languages, the part between braces in the literal string in a println! is not a string escape, meaning that you could put any (printable) Rust expression. Instead, you should see it as a named parameter. Therefore, it can only be a variable name. println! also allows you to mimic variable definition if you don't already have a variable with that name, like this:
println!("{px}", px = p.x);
Another similar but different issue is trying to println! when you have a Rust variable that is an underscore:
let _ = 2;
println!("Value of _ is {_}");
The error is:
invalid format string: invalid argument name `_`
...
println!("Value of _ is {_}");
^ invalid argument name in format string
This is probably because the underscore is meant to be ignored
The underscore (_) is a reserved identifier in Rust and serves different purposes depending on the context. It usually means that something is ignored.
Other sites explain how in rust, the underscore does "not bind to the variable"; or "does not have ownership of the object"
See #jthulhu's comment --not only is _ meant to be ignored in Rust, the _ variable cannot be used in Rust; therefore isn't even allocated (that explains my error messages). This is different from other programming languages, where the _ variable is just a convention to signify the _ variable is meant to be ignored but could be used. In Rust, as #jthulhu says: "Basically, doing let _ = 3; will result in absolutely nothing even without optimizations"
I know the OP already found the best accepted answer. I just want to offer this answer in case other folks search for "invalid format string" or "invalid argument name _" and find this SO question.
If you tried a workaround like:
println!("Value of _ is {}", _);
Then you get a new error, reinforcing the idea that the underscore variable is never meant to be used:
in expressions, `_` can only be used on the left-hand side of an assignment
`_` not allowed here
So the underscore variable is allowed on the left-hand side of an assignment as in my code above, or this underscore expression where the _ signifies a placeholder in a destructuring assignment, but you can't reference the underscore variable later, including in println!. The only solution is to use a different variable name.

Unwrap_or need String or &str in rust?

I am trying to parse an environment value; using std::env as follows
let environment = env::var("SENSIBULL_ENVIRONMENT").unwrap();
This returns a string to the variable environment. If I want to pass a default value, then I have to use the below code
let environment = env::var("SENSIBULL_ENVIRONMENT").unwrap_or("development".into());
but I was expecting to do it like
let environment = env::var("SENSIBULL_ENVIRONMENT").unwrap_or("development");
as mentioned in the Rust example
But then it shows error String expected but found &str
Why is that error not happening in the example code?
In Rust, string literals are of type &str, and environment variables are of type String. Essentially, &strs have a fixed size, while Strings can be dynamically changed and resized (read more here). unwrap_or requires that the alternate value be of the same type as the option value, which means that you must provide a String to match the type of the environment variable. That's why you have to call .into to convert it to a string. In their example, the option uses a string literal, which is also of type &str, so there are no errors.

How to pass a variable as a string literal in Rust?

I am using a macro hex! that accepts only string literals.
I have a value returned from a function and stored in a variable. I cannot hard-code the values and call this function. So, how can I call the hex! macro with a variable?
This is my working code:
let account: AccountId32 = hex_literal::hex!["d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"].into();
and this is the code where I am facing the error:
let account_id = "d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d";
let account: AccountId32 = hex_literal::hex!(&account_id).into();
The error is:
error: proc-macro derive panicked
--> src/examples/example_substratekitties.rs:49:32
|
49 | let account: AccountId32 = hex_literal::hex!(&account_id).into();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: message: expected one string literal
= note: this warning originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
All the examples of the hex! macro only demonstrate it with string literals.
You cannot. It is literally impossible. A literal is something where the value is written verbatim in the source code of the program.
Don't use that macro because it doesn't do what you want (emphasis mine):
This crate provides hex! macro for converting hexadecimal string literal to byte array at compile time.
Perhaps you want the hex crate?
See also:
What is the difference between literals and non-literals, other than the fact that non-literals go into the heap?
What does the word "literal" mean?
How can I convert a hex string to a u8 slice?
Converting a hexadecimal string to a decimal integer
Decimal number to hexadecimal string
How to convert hex string to a float in Rust?
How do I convert a string to hex in Rust?
Show u8 slice in hex representation

Formatting a const string in Rust [duplicate]

This extremely simple Rust program:
fn main() {
let c = "hello";
println!(c);
}
throws the following compile-time error:
error: expected a literal
--> src/main.rs:3:14
|
3 | println!(c);
| ^
In previous versions of Rust, the error said:
error: format argument must be a string literal.
println!(c);
^
Replacing the program with:
fn main() {
println!("Hello");
}
Works fine.
The meaning of this error isn't clear to me and a Google search hasn't really shed light on it. Why does passing c to the println! macro cause a compile time error? This seems like quite unusual behaviour.
This should work:
fn main() {
let c = "hello";
println!("{}", c);
}
The string "{}" is a template where {} will be replaced by the next argument passed to println!.
TL;DR If you don't care why and just want to fix it, see the sibling answer.
The reason that
fn main() {
let c = "hello";
println!(c);
}
Cannot work is because the println! macro looks at the string at compile time and verifies that the arguments and argument specifiers match in amount and type (this is a very good thing!). At this point in time, during macro evaluation, it's not possible to tell that c came from a literal or a function or what have you.
Here's an example of what the macro expands out to:
let c = "hello";
match (&c,) {
(__arg0,) => {
#[inline]
#[allow(dead_code)]
static __STATIC_FMTSTR: &'static [&'static str] = &[""];
::std::io::stdio::println_args(&::std::fmt::Arguments::new(
__STATIC_FMTSTR,
&[::std::fmt::argument(::std::fmt::Show::fmt, __arg0)]
))
}
};
I don't think that it's actually impossible for the compiler to figure this out, but it would probably take a lot of work with potentially little gain. Macros operate on portions of the AST and the AST only has type information. To work in this case, the AST would have to include the source of the identifier and enough information to determine it's acceptable to be used as a format string. In addition, it might interact poorly with type inference - you'd want to know the type before it's been picked yet!
The error message asks for a "string literal". What does the word "literal" mean? asks about what that means, which links to the Wikipedia entry:
a literal is a notation for representing a fixed value in source code
"foo" is a string literal, 8 is a numeric literal. let s = "foo" is a statement that assigns the value of a string literal to an identifier (variable). println!(s) is a statement that provides an identifier to the macro.
If you really want to define the first argument of println! in one place, I found a way to do it. You can use a macro:
macro_rules! hello {() => ("hello")};
println!(hello!());
Doesn't look too useful here, but I wanted to use the same formatting in a few places, and in this case the method was very helpful:
macro_rules! cell_format {() => ("{:<10}")}; // Pads with spaces on right
// to fill up 10 characters
println!(cell_format!(), "Foo");
println!(cell_format!(), 456);
The macro saved me from having to duplicate the formatting option in my code.
You could also, obviously, make the macro more fancy and take arguments if necessary to print different things with different arguments.
If your format string will be reused only a moderate number of times, and only some variable data will be changed, then a small function may be a better option than a macro:
fn pr(x: &str) {
println!("Some stuff that will always repeat, something variable: {}", x);
}
pr("I am the variable data");
Outputs
Some stuff that will always repeat, something variable: I am the variable data

Resources