let a = [10, 20, 30, 40, 50];
let mut index_ = 0;
while index_ < 5 {
println!("{}", a[index_]); // works
println!("{a[index_]}"); // does not work
println!("{index_}"); // works
println!("{}", index_); // works
index_ = index_ + 1;
}
Why does "{a[index_]}" not work? It seems like it should to me.
The documentation says that this syntax is called "named parameters", and it supports names, not arbitrary expressions.
If a named parameter does not appear in the argument list, format! will reference a variable with that name in the current scope.
a[index_] is not a valid name (but is a valid expression), so you get the error because the format! syntax doesn't let you use arbitrary expressions inside {}, like in Python. Note that println! "uses the same syntax as format!", so the same reasoning applies to println! as well.
To explain why the named parameters syntax is restricted, I'll first note that the "{ident}" syntax existed before Rust 1.58 allowed it to access variables. It was originally designed to work like this:
println!("{name} bought {amount:.2} kg of {nouns}.",
name = "Richard",
amount = 5.0,
nouns = "apples",
);
So the syntax is not new, it was simply relaxed to look for other identifiers in-scope as a fallback if a named parameter wasn't passed to the macro.
They actually can't make the format macros take arbitrary expressions. Of course they can do whatever they like, however it would violate a restriction set elsewhere in the name of forward compatibility.
Let's say I tried to make a macro that worked similarly to the named parameter syntax but allowed for any expression while still allowing for other formatting parameters. Below is a trivial example but the compiler rejects it:
macro_rules! my_format {
($e:expr : $width:literal) => {
// ..
};
}
fn main() {
my_format!(expr:2);
}
error: `$e:expr` is followed by `:`, which is not allowed for `expr` fragments
--> src/main.rs:2:14
|
2 | ($e:expr : $width:literal) => {
| ^ not allowed after `expr` fragments
|
= note: allowed there are: `=>`, `,` or `;`
Why is this? If we look at Follow-set Ambiguity Restrictions:
The parser used by the macro system is reasonably powerful, but it is limited in order to prevent ambiguity in current or future versions of the language. In particular, in addition to the rule about ambiguous expansions, a nonterminal matched by a metavariable must be followed by a token which has been decided can be safely used after that kind of match.
As an example, a macro matcher like $i:expr [ , ] could in theory be accepted in Rust today, since [,] cannot be part of a legal expression and therefore the parse would always be unambiguous. However, because [ can start trailing expressions, [ is not a character which can safely be ruled out as coming after an expression. If [,] were accepted in a later version of Rust, this matcher would become ambiguous or would misparse, breaking working code. Matchers like $i:expr, or $i:expr; would be legal, however, because , and ; are legal expression separators. The specific rules are:
expr and stmt may only be followed by one of: =>, ,, or ;.
So the developers have not ruled out that : may be some expression trailer or joiner in the future and would like to reserve the syntax if needed. Even if you try to parse it with syn in a procedural macro to work around this restriction, it will actually try to parse as an ExprType (like foo: f64) which is part of the Type Ascription RFC. While that RFC was mostly implemented, it is set to be removed, but I mention it as an example of evolving syntax. If they were to allow the formatting argument to be an arbitrary expression, they'd have to stabilize that : is a valid expression separator.
Unless I'm missing some other syntax, : probably could be stabilized as a separator, and perhaps this is a motivating case. But only the future will tell.
Another hurdle that'd have to be dealt with is that blocks ({ ... }) are also expressions and yet there is an existing syntax for {{ and }} in the formatting macros; they are used for escaping. So if any expression were allowed, then "{{expr}}" would be ambiguous.
Related
This maplit crate allows hashmap literals with => as a separator. I believe it's impossible to use macro_rules! to allow the : separator, but is it possible with a macro that processes a stream of tokens?
Is it impossible to add { key: value } style macros to the language, even in the compiler?
I suspect the problem is conflict with the : type operator, but I cannot construct an ambiguous example where it is impossible for the compiler to make a decision of which way to interpret :.
#kmdreko does a good job of explaining why you can't have an expression followed by a colon in a macro. However, you can work around that, by forcing the key to be a token tree rather than an expression:
macro_rules! hashmap{
( $($key:tt : $val:expr),* $(,)? ) =>{{
#[allow(unused_mut)]
let mut map = ::std::collections::HashMap::with_capacity(hashmap!(#count $($key),* ));
$(
#[allow(unused_parens)]
let _ = map.insert($key, $val);
)*
map
}};
(#replace $_t:tt $e:expr ) => { $e };
(#count $($t:tt)*) => { <[()]>::len(&[$( hashmap!(#replace $t ()) ),*]) }
}
playground
The drawback of taking this route, as opposed to a procedural macro, which probably could handle this pattern just fine, is that most complex expressions will have to be wrapped in parentheses or curly brackets. For example,
let map = hashmap!{ 2 + 2 : "foo" };
won't work, but
let map = hashmap!{ (2 + 2) : "foo" };
will.
If you were to replace the => with a : in the maplit! macro you'd get this error:
error: `$key:expr` is followed by `:`, which is not allowed for `expr` fragments
--> src/lib.rs:6:17
|
6 | ($($key:expr: $value:expr),*) => {
| ^ not allowed after `expr` fragments
|
= note: allowed there are: `=>`, `,` or `;`
The specific problem is that the macro accepts any arbitrary expression as the key type. You could still have the { key: value } syntax, but only if the key is an ident (for example) instead of an expr.
You can read the Follow-set Ambiguity Restrictions section of Macros by Example in the Rust Reference:
The parser used by the macro system is reasonably powerful, but it is limited in order to prevent ambiguity in current or future versions of the language.
[...]
expr and stmt may only be followed by one of: =>, ,, or ;.
That's not to say its necessarily ambiguous, though it may get confusing to parse in examples with a lot of :s like MyEnum::Variant: ::std::collections::Vec::new(). Its designed like that because only the documented tokens are guaranteed to indicate the end of an expression and other tokens may become ambiguous in the future.
You'll see though, that this only limits how macro_rules! patterns are handled and would not affect a procedural macro since you have free reign to decide how the tokens are parsed.
I tried to create a simple procedural macro using syn to aid in parsing expressions, but it chokes on the Expr : Expr syntax because it attempts to eagerly parse the first expression as a type ascription expression which is a yet-to-be-completed feature. This is the kind of future expansion that the macro_rules! guards against.
Going with a token tree and using parenthesis for ambiguity in Aiden4's answer is the way to go if you really want : instead of =>. This still could be done with a procedural macro, but it would not be as readily available and would be ambiguous if type ascription expressions gets added to the language.
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
I have a tiny playground example here
fn main() {
let l = Some(3);
match &l {
None => {}
Some(_x) => {} // x is of type &i32
}
}
I'm pattern matching on &Option and if I use Some(x) as a branch, why is x of type &i32?
The type of the expression &l you match against is &Option<i32>, so if we are strict the patterns should be &None and &Some(x), and if we use these patterns, the type of x indeed is i32. If we omit the ampersand in the patterns, as you did in your code, it first looks like the patterns should not be able to match at all, and the compiler should throw an error similar to "expected Option, found reference", and indeed this is what the compiler did before Rust version 1.26.
Current versions of Rust support "match ergonomics" introduced by RFC 2005, and matching a reference to an enum against a pattern without the ampersand is now allowed. In general, if your match expression is only a reference, you can't move any members out of the enum, so matching a reference against Some(x) is equivalent to matching against the pattern &Some(ref x), i.e. x becomes a reference to the inner value of the Option. In your particular case, the inner value is an i32, which is Copy, so you would be allowed to match against &Some(x) and get an i32, but this is not possible for general types.
The idea of the RFC is to make it easier to get the ampersands and refs in patterns right, but I'm not completely convinced whether the new rules actually simplified things, or whether they added to the confusion by making things magically work in some cases, thereby making it more difficult for people to get a true understanding of the underlying logic. (This opinion is controversial – see the comments.)
I read the below syntax from byteorder:
rdr.read_u16::<BigEndian>()
I can't find any documentation which explains the syntax instance.method::<SomeThing>()
This construct is called turbofish. If you search for this statement, you will discover its definition and its usage.
Although the first edition of The Rust Programming Language is outdated, I feel that this particular section is better than in the second book.
Quoting the second edition:
path::<...>, method::<...>
Specifies parameters to generic type, function, or method in an expression; often referred to as turbofish (e.g., "42".parse::<i32>())
You can use it in any kind of situation where the compiler is not able to deduce the type parameter, e.g.
fn main () {
let a = (0..255).sum();
let b = (0..255).sum::<u32>();
let c: u32 = (0..255).sum();
}
a does not work because it cannot deduce the variable type.
b does work because we specify the type parameter directly with the turbofish syntax.
c does work because we specify the type of c directly.
I have this enum:
enum Foo {
Bar,
Baz(String),
// And maybe some other variants
}
I find myself constantly pattern matching against predefined strings of Baz.
match foo_instance {
Foo::Baz(ref x) => match x.as_str() {
"stuff" => ...,
"anotherstuff" => ...,
},
// Other match cases...
}
Sometimes I only match for one case, sometimes 4-5 cases of Foo::Baz. In latter situation, a double match doesn't bother me so much, in fact the grouping makes sense at that point. If I only match against one case of Foo::Baz it doesn't feel right. What I really want to be able to do is this:
const STUFF: Foo = Foo::Baz("stuff".to_string());
const ANOTHER_STUFF: Foo = Foo::Baz("anotherstuff".to_string());
match foo_instance {
&STUFF => ...,
&ANOTHER_STUFF => ...,
// Other match cases...
}
But of course, because of to_string() calls, this won't work (and I also need to derive Eq trait to be able to match against consts which is odd. That may also be a problem for me.). Is there any way to mimic this? For example, using a macro, can I do something like that:
const STUFF: Foo = magic!(Foo::Baz("stuff".to_string());
const ANOTHER_STUFF: Foo = magic!(Foo::Baz("anotherstuff".to_string()));
// Or any other thing that can mimic top level behavior,
// I may create these constants dynamically at program start, that would work too.
match foo_instance {
another_magic!(STUFF) => ...,
another_magic!(ANOTHER_STUFF) => ...,
// Other match cases...
}
Generally speaking, I want to be able to have some constant variants of an enum that contains an heap allocated data (String in this case), so that I can reuse them whenever I need in a match case. What's the best way to deal with this?
You cannot create constants with heap-allocated data in current Rust, full stop.
Maybe in the future it will be possible to create a const FOO: String, but there's a lot of work to be done and decisions to be made before that.
using a macro
Macros are not magic. They only let you write certain types of code that you can already write but with a new syntax. Since you cannot create these constants, you cannot write a macro to create them either.
I want to somehow extract all the left hand side of the match case to a constant or something like that. Match guards doesn't help me with that(Yes, it eliminates the need for double match, but its a last resort for me.
Also map()ing my enum is not going to work, because it's not a struct, I need to match in map too.
Creating a parallel type seems too weird.
I can create a const static &str and match against that with those solutions but that would raise another problem. These constants will only have strings and they lack the total meaning.
There is only one solution that does what I need along those solutions presented and I found that one too verbose.
I have a lot of enums like this and it will be hard to create parallel types for each of them and apart from that, the solution is just verbose.
For other readers who can use the alternative solutions which you have already discarded, see also:
How do I match a String in a struct with a constant value?
For further reading, see also:
Trying to declare a String const results in expected type, found "my string"
Expected String, found &str when matching an optional string
How to match a String against string literals in Rust?