2015-01-01 24 views
33

這種極其簡單的防鏽方案:println!錯誤:預期文字/格式參數必須是一個字符串文字

fn main() { 
    let c = "hello"; 
    println!(c); 
} 

拋出以下編譯時錯誤:

error: expected a literal 
--> src/main.rs:3:14 
    | 
3 |  println!(c); 
    |   ^

生鏽的早期版本中,錯誤說:

error: format argument must be a string literal. 
    println!(c); 
      ^

與更換計劃

fn main() { 
    println!("Hello");  
} 

工作正常。

這個錯誤的含義對我來說並不清楚,谷歌搜索也沒有真正揭示它。爲什麼將c傳遞給println!宏會導致編譯時錯誤?這似乎很不尋常的行爲。

回答

25

之所以

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的部分操作,我認爲它只有類型信息。爲了在這種情況下工作,AST必須包括標識符的來源和足夠的信息以確定它是「安全的」。另外,它可能與類型推斷相互作用很差 - 在選取它之前,你想知道它的類型!

從我在其他答案的評論:

錯誤消息要求一個「字符串文字」。有一個SO問題什麼意思,它鏈接到Wikipedia entry

a literal is a notation for representing a fixed value in source code

"foo"是一個字符串文字,8是數字文字。 let s = "foo"是一個語句,它將字符串的值賦給一個標識符(變量)。 println!(s)是爲宏提供標識符的語句。

+1

爲什麼編譯時不可能知道c在編譯時是從字面上來的? –

+0

我認爲真正的問題是,如果'c =「{}」'和宏在編譯時被解析,那麼編譯器確實很難確定它是否會有更多的參數被預期,所以驗證不會在編譯時無法保證... – evotopid

+3

我認爲這不是不可能*永遠*,現在只是宏如何實現。宏採取[部分AST](http://doc.rust-lang.org/guide-macros.html),我認爲它只有類型信息。你的問題將涉及知道類型的來源和足夠的信息,以確定它的「安全」。另外,它可能與類型推斷相互作用不佳 - 你想知道它在被選中之前的類型! – Shepmaster

32

這應該工作:

fn main() { 
    let c = "hello"; 
    println!("{}", c); 
} 

字符串"{}"就是{}將被傳遞給println!下一個參數被替換的模板。

+2

雖然它有點難看,但確實有效!爲什麼最初的錯誤發生?以這種方式打印字符串在幾乎所有現代語言中都是非常常見的任務,給出的錯誤信息實際上是不明確的。 –

+2

這不是有意義的,但它確實讓你知道什麼是「文字」。 '「foo」'是一個字符串文字。 '8'是一個數字文字。 'let s =「foo」'將字符串的值賦給變量,'println!(s)'將變量傳遞給宏。 – Shepmaster

+1

@Shepmaster謝謝 - 你能提供一個答案提供一些更詳細的信息嗎?我預計很多其他的防火牆用戶會遇到來自像Python這樣的語言的這個錯誤,並且有您的解釋會很有幫助。 –

2

如果你確實想定義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); 

這個宏讓我不必複製我的代碼中的格式選項。

顯然,你可以使宏更加花哨,並在必要時使用不同的參數來打印不同的東西。

+1

如果您打算採用宏觀方法,您可以將'println!'放入宏中,就像'macro_rules! cell_format {($ e:expr)=>(println!(「{:<10}」,$ e)); }' – mattforni

1

如果您的格式字符串將被重用只能算是中等的次數,只有一些變量數據將被改變,那麼一個小功能可能比宏更好的選擇:

fn pr(x){ 
    println!("Some stuff that will always repeat, something variable: {}",x); 
}; 

pr("I am the variable data".to_string()); 

輸出

Some stuff that will always repeat, something variable: I am the variable data

相關問題