コンパイルエラー発生!
このコードを実装したら、コンパイルエラーになりました。
Rust初心者の私には、このエラーの理屈が分からなくて、悩んだので書き留めておきます。
fn print_value<T: std::fmt::Debug>(s: &T) {
println!("{:?}", s);
}
let s: &str = "Hello, world!";
// エラーになった
// the size for values of type `str` cannot be known at compilation time
print_value(s);
なんでエラーになるの?
s
は&str
型なのに、str
型と認識される理由が分からなかったです。。。。
まずはstr型について
まずはstr
型についておさらいです。
The Rust Programming Language 日本語版によると&str
はstr
型の参照ですが、&str
はスライス型です。
"Hello"などの文字列リテラルもスライス型になるようです。
let s: &str = "Hello";
ここで注意です。&String
と&str
はまったく別の型です。
- &String: String型の参照
- &str: str型の参照
str型は変数、引数、戻り値の型として指定できません
この変数宣言はコンパイルエラーになります。
let s: str;
str
型はコンパイル時点でデータサイズが未定なので使えません。
しかし、&str
にすると、参照値(アドレス値)となりデータサイズが分かるので使えます。
下図はVSCODEのスクショです。
size=16とデータサイズが16バイトであると分かります。
次にジェネリック型の型解決の動き
これを理解していなかったのでエラーの理由がわかりませんでした。
こちらのコードは、問題なくコンパイルできます。
let n:i32 = 10;
print_value(&n);
では、ジェネリックがどのように型を解決(推論)してくか。です。
- T = i32 (まずはTの型を解決)
- &T = &i32
と型解決します。
この2段階解決だと知りませんでした。
私は
- &T = &i32
と一発で解決すると思っていました。
この解決の動きだと下記の&str
の場合は
let s: &str = "Hello, world!";
print_value(s);
- T = str
- &T = &str
となります。
そうです、1段階のT = strの時点で、T
がstr
となり、str
は引数の型としては使えないのでダメ!とエラーになるのです。
このエラーを回避するなら
let s: &str = "Hello, world!";
print_value(&s);
とします。すると
-
T = &str(
&s
の型は&&str
なので、Tの型は&str
になる) - &T = &&str
となり、1段階目のTが&str
になるのでエラーになりません。