Println! error: аргумент format должен быть строковым литералом
Эта чрезвычайно простая программа Rust:
fn main() {
let c = "hello";
println!(c);
}
выдает следующую ошибку времени компиляции:
main.rs:15:14: 15:15 error: format argument must be a string literal.
main.rs:15 println!(c);
^
note: in expansion of format_args!
<std macros>:2:54: 2:77 note: expansion site
<std macros>:1:1: 3:2 note: in expansion of println!
main.rs:15:5: 15:17 note: expansion site
error: aborting due to previous error
Could not compile `numrust`.
Замена программы:
fn main() {
println!("Hello");
}
Прекрасно работает.
Значение этой ошибки для меня непонятно, и поиск Google на самом деле не проливает свет на него. Почему передача c
в макрос println!
вызывает ошибку времени компиляции? Это кажется довольно необычным поведением.
Ответы
Ответ 1
Причина, по которой
fn main() {
let c = "hello";
println!(c);
}
Невозможно работать, потому что макрос println!
смотрит на строку во время компиляции и проверяет соответствие аргументов и аргументов по количеству и типу (это очень хорошо!). В этот момент времени, во время макрооценки, невозможно сказать, что c
пришел из литерала или функции или что у вас есть.
Вот пример того, что макрос расширяется:
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)]
))
}
};
Из моего комментария к этому ответу:
Я не думаю, что для компилятора это вообще невозможно понять, но это, вероятно, потребует большой работы (потенциально для небольшого выигрыша). Макросы работают на участках АСТ, которые, как я предполагаю, имеют только информацию о типе. Чтобы работать в этом случае, AST должен был бы включить источник идентификатора и достаточную информацию, чтобы определить его "безопасным". Кроме того, он может плохо взаимодействовать с типом вывода - вы бы хотели знать тип до его выбора!
Из моего комментария к другому ответу:
Сообщение об ошибке запрашивает "строковый литерал". Там fooobar.com/questions/94991/... вопрос о том, что это означает, который ссылается на запись в Википедии:
литерал является обозначением для представления фиксированного значения в исходном коде
"foo"
- строковый литерал, 8
- числовой литерал. let s = "foo"
- это оператор, который присваивает значение строкового литерала идентификатору (переменной). println!(s)
- это оператор, который предоставляет идентификатор макроса.
Ответ 2
Это должно работать:
fn main() {
let c = "hello";
println!("{}", c);
}
Строка "{}"
- это шаблон, где {}
будет заменен следующим аргументом, переданным в println!
.
Ответ 3
Если вы действительно хотите определить первый аргумент println! в одном месте я нашел способ сделать это. Вы можете использовать макрос:
macro_rules! hello {() => ("hello")};
println!(hello!());
Не выглядит слишком полезным здесь, но я хотел использовать одно и то же форматирование в нескольких местах, и в этом случае метод был очень полезен:
macro_rules! cell_format {() => ("{:<10}")}; // Pads with spaces on right
// to fill up 10 characters
println!(cell_format!(), "Foo");
println!(cell_format!(), 456);
Макрос спас меня от необходимости дублировать опцию форматирования в моем коде.
Вы также, очевидно, могли бы сделать макрос более причудливым и принимать аргументы, если необходимо, для печати разных вещей с разными аргументами.
Ответ 4
Если ваша строка формата будет повторно использоваться только умеренное количество раз, и только некоторые переменные данные будут изменены, то небольшая функция может быть лучшим вариантом, чем макрос:
fn pr(x){
println!("Some stuff that will always repeat, something variable: {}",x);
};
pr("I am the variable data".to_string());
Выходы
Некоторые вещи, которые всегда будут повторяться, что-то переменное: Я - переменные данные