2015-01-01 29 views
33

この非常に簡単な錆プログラム:錆の以前のバージョンではprintln!エラー:リテラル/フォーマット引数は文字列リテラルでなければならないと予想

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

、次のコンパイル時にエラーがスローされます

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

エラーは言った:

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

はしてプログラムを置き換えます

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

正常に動作します。

このエラーの意味は私には分かりません.Google検索では実際にそのことを明らかにしていません。 cprintln!マクロに渡すと、コンパイル時にエラーが発生するのはなぜですか?これは非常に珍しい動作のようです。

答えて

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は識別子のソースとそれが「安全」であるかを判断するのに十分な情報を含める必要があります。さらに、型推論とのやりとりが難しいかもしれません - 型を知りたければ、それはまだ選択されていません。

他の答えに私のコメントから:

エラーメッセージは、「文字列リテラル」を要求します。 Wikipedia entryにリンクそれが何を意味するのかについてSO質問が、あります:

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

関連する問題