Rustエラー[E0308]: mismatched types — String vs &str の修正方法

beginner🦀 Rust2026-04-15| Rust 1.x(全バージョン)、Linux・macOS・Windows 対応

Error Message

error[E0308]: mismatched types: expected struct `String`, found `&str`
#rust#string##変換

エラー内容

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[..];      // フルスライス

Related Error Notes