Ce programme extrêmement simple Rust:
fn main() {
let c = "hello";
println!(c);
}
renvoie l'erreur de compilation suivante:
error: expected a literal
--> src/main.rs:3:14
|
3 | println!(c);
| ^
Dans les versions précédentes de Rust, l'erreur indiquait:
error: format argument must be a string literal.
println!(c);
^
Remplacement du programme par:
fn main() {
println!("Hello");
}
Fonctionne bien.
La signification de cette erreur n'est pas claire pour moi et une recherche Google n'a pas vraiment mis la lumière dessus. Pourquoi passer c
à println!
macro provoque une erreur de temps de compilation? Cela semble être un comportement assez inhabituel.
TL; DR Si vous ne vous souciez pas pourquoi et que vous voulez juste le réparer, voir la réponse du frère .
La raison pour laquelle
fn main() {
let c = "hello";
println!(c);
}
Ne fonctionne pas car la macro println!
Examine la chaîne au moment de la compilation et vérifie que les arguments et les spécificateurs d'arguments correspondent en quantité et en type (C'est une très bonne chose!). À ce stade, lors de l'évaluation des macros, il n'est pas possible de dire que c
provenait d'un littéral ou d'une fonction ou de ce que vous avez.
Voici un exemple de l'extension de la macro:
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)]
))
}
};
Je ne pense pas qu'il soit réellement impossible pour le compilateur de comprendre cela, mais cela prendrait probablement beaucoup de travail (potentiellement pour peu de gain) . Les macros fonctionnent sur des parties de l'AST, qui, je suppose, ne contiennent que des informations de type. Pour fonctionner dans ce cas, le AST devrait inclure la source de l'identifiant et suffisamment d'informations pour déterminer qu'il est "sûr". De plus, il pourrait mal interagir avec l'inférence de type - vous voulez connaître le type avant qu'il ne soit encore choisi!
Le message d'erreur demande un "littéral de chaîne". Il y a une DONC question sur ce que cela signifie, qui renvoie à la entrée Wikipedia :
un littéral est une notation pour représenter une valeur fixe dans le code source
"foo"
Est un littéral de chaîne, 8
Est un littéral numérique. let s = "foo"
Est une instruction qui affecte la valeur d'un littéral de chaîne à un identifiant (variable). println!(s)
est une instruction qui fournit un identifiant à la macro.
Cela devrait fonctionner:
fn main() {
let c = "hello";
println!("{}", c);
}
La chaîne "{}"
est un modèle où {}
sera remplacé par le prochain argument passé à println!
.
Si votre chaîne de format ne sera réutilisée qu'un nombre modéré de fois et que seules certaines données variables seront modifiées, une petite fonction peut être une meilleure option qu'une macro:
fn pr(x: &str) {
println!("Some stuff that will always repeat, something variable: {}", x);
}
pr("I am the variable data");
Les sorties
Des trucs qui se répéteront toujours, quelque chose de variable: je suis les données variables
Si vous voulez vraiment définir le premier argument de println! en un seul endroit, j'ai trouvé un moyen de le faire. Vous pouvez utiliser une macro:
macro_rules! hello {() => ("hello")};
println!(hello!());
Ne semble pas trop utile ici, mais je voulais utiliser le même formatage à quelques endroits, et dans ce cas, la méthode était très utile:
macro_rules! cell_format {() => ("{:<10}")}; // Pads with spaces on right
// to fill up 10 characters
println!(cell_format!(), "Foo");
println!(cell_format!(), 456);
La macro m'a évité d'avoir à dupliquer l'option de formatage dans mon code.
Vous pouvez également, évidemment, rendre la macro plus sophistiquée et prendre des arguments si nécessaire pour imprimer différentes choses avec des arguments différents.