エラー内容
error[E0308]: mismatched types: expected struct `String`, found `&str`
または逆のケース:
error[E0308]: mismatched types: expected `&str`, found struct `String`
これは、Rustを始めたばかりの開発者が最初につまずく壁のひとつです。Stringと&strはソースコード上ではほぼ同じように見えますが、コンパイラにとっては完全に異なる型です。Rustはこれらを暗黙的に変換しません。
なぜこのエラーが起きるのか
Stringはヒープに確保され、所有権を持ちます。データを自身で保持しています。一方、&strはどこか別の場所に存在する文字列データへの借用参照にすぎません。全く異なるものです。コンパイラはこの境界を厳密に強制するため、型の不一致はランタイムではなくコンパイル時に弾かれます。
よくあるケース:
- 文字列リテラル(
&str)をStringを受け取る関数に渡す - 所有権のある
Stringを&strを受け取る関数に渡す String型のstructフィールドにリテラルを代入する- 戻り値の型が
Stringの関数からリテラルをreturnする
クイックフィックス — ケース別対処法
ケース1:&strを持っているが、Stringが必要な場合
.to_string()、.to_owned()、またはString::from()を呼び出します:
// 変更前 — コンパイルエラー
fn greet(name: String) {
println!("Hello, {}!", name);
}
fn main() {
greet("Alice"); // error: expected String, found &str
}
// 変更後 — 修正済み
fn main() {
greet("Alice".to_string());
// または
greet("Alice".to_owned());
// または
greet(String::from("Alice"));
}
3つとも同じ結果になります。実際のRustコードでは.to_string()が最もよく使われます。
ケース2:Stringを持っているが、&strが必要な場合
&で借用するか、.as_str()を呼び出します:
fn print_name(name: &str) {
println!("{}", name);
}
fn main() {
let name = String::from("Alice");
print_name(&name); // deref coercion: &String → &str
print_name(name.as_str()); // 明示的、同じ効果
print_name(&name[..]); // スライス構文 — 動くが、あまり見かけない
}
&nameが機能するのは、Rustが&Stringから&strへと*deref coercion(参照外し型強制)*を暗黙的に適用するためです。コピーは発生しません。これが慣用的なアプローチです。
ケース3:Stringとして型付けされたstructフィールドにリテラルを代入する場合
struct Config {
host: String,
}
// エラー
let c = Config { host: "localhost" }; // expected String, found &str
// 修正済み
let c = Config { host: "localhost".to_string() };
let c = Config { host: String::from("localhost") };
ケース4:戻り値の型がStringなのにリテラルをreturnしている場合
// エラー
fn get_default() -> String {
"unknown" // expected String, found &str
}
// 修正済み
fn get_default() -> String {
"unknown".to_string()
// または: String::from("unknown")
}
ケース5:Vecにリテラルをpushする場合
// エラー
let mut names: Vec = Vec::new();
names.push("Alice"); // expected String, found &str
// 修正済み
names.push("Alice".to_string());
names.push(String::from("Bob"));
より良い方法:関数で&strを受け取る
関数シグネチャを自分で定義できる場合で、文字列を保存したり変更したりする必要がなければ、パラメータの型をStringではなく&strに変更しましょう。coercionによってリテラルも所有権のある文字列も受け取れるため、呼び出し側で変換が不要になります:
// 制限的 — 呼び出し側は所有権のあるStringを渡す必要がある
fn greet(name: String) { ... }
// 柔軟 — リテラルも&Stringも受け取れる
fn greet(name: &str) { ... }
// どちらも.to_string()なしでコンパイルが通る:
greet("Alice"); // &strリテラル — OK
greet(&owned_string); // &String — coercionでOK
Rust APIガイドラインでは、読み取り専用の文字列パラメータには&strを推奨しています。小さな変更ですが、呼び出し側の無駄なアロケーションをなくせます。
修正の確認
プロジェクトをリビルドします:
cargo build
正常なビルドは以下のようになります:
Compiling myproject v0.1.0
Finished dev [unoptimized + debuginfo] target(s) in 0.42s
出力にerror[E0308]がなければ、型の不一致は解消されています。単一ファイルの簡易確認にはrustc main.rsも使えます。
どれをいつ使うか
.to_string()—&strからStringへの変換の定番。読みやすく、慣用的。.to_owned()— 同じ結果、Borrow型なら何でも使える。好むスタイルガイドもある。String::from()— 明示的な構築。意図を明確にしたいときに有効。&stringまたは.as_str()— 所有から借用へ。コストゼロ、アロケーションなし。
クイックリファレンス
// &str → String
let s: String = "hello".to_string();
let s: String = "hello".to_owned();
let s: String = String::from("hello");
let s: String = format!("hello"); // フォーマットも必要な場合はこれを使う
// String → &str
let r: &str = &owned; // deref coercion
let r: &str = owned.as_str(); // 明示的
let r: &str = &owned[..]; // フルスライス

