Rustのエラー E0308: mismatched types(型の不一致)
E0308 は、Rust がある型の値を期待していたのに、違う型の値が渡されたという意味のエラーです。Rust でいちばんよく目にするエラーかもしれません。でも安心してください。メッセージにはほぼ必ず2つの型が書かれているので、たいていすぐに直せます。(ほかのエラーを探しているなら、Rustのコンパイルエラー解説を見てください。)
エラーメッセージ
Section titled “エラーメッセージ”error[E0308]: mismatched types --> src/main.rs:6:29 |6 | let user = User { name: "Ferris" }; | ^^^^^^^^ expected `String`, found `&str` |help: try using a conversion method |6 | let user = User { name: "Ferris".to_string() }; | ++++++++++++「mismatched types」は「型が一致しない」という意味です。String を期待していたのに &str が見つかった、と言っていて、help は「変換メソッドを使ってみては」と .to_string() を提案しています。
このエラーが出るのは、次のプログラムです。
struct User { name: String,}
fn main() { let user = User { name: "Ferris" }; println!("{}", user.name);}エラーの意味
Section titled “エラーの意味”E0308 のメッセージは、いつも同じ形をしています。expected A, found B(A を期待したが、B が見つかった)です。
- expected(期待された型)は、その場所で Rust が必要としている型です。この型はどこか別の場所から決まります。型注釈、関数の引数や戻り値の型、構造体のフィールド、あるいは
if/matchのほかの分岐などです。 - found(見つかった型)は、あなたが実際にそこに書いた値の型です。
この例では、name フィールドが String として宣言されているので、Rust は String を期待します。ところが "Ferris" は文字列リテラルで、型は &str です。この2つは関係の深い型ですが、同じ型ではありません。
help: の行もぜひ見てください。E0308 では、コンパイラが上の .to_string() のように、そのまま使える直し方を教えてくれることがよくあります。
Rust がこうする理由
Section titled “Rust がこうする理由”Rust は、型と型の間の変換を黙って行うことがありません。言語によっては、数値がいつの間にか文字列になったり、大きな数が小さな型に収まるようにこっそり切り詰められたりします。こうした見えない変換は、データの消失や間違った計算結果、本当の原因から遠く離れた場所でのクラッシュといった、本物のバグを生みます。
そこで Rust は、すべての変換をコードの上に見える形で書かせます。少しだけタイプ量は増えますが、あとでコードを読んだときに、どこで値の型が変わるのか、その変換が失敗しうるのかがひと目でわかります。型そのものについてはデータ型を見てください。
それぞれの直し方の最初に、エラーの形を載せています。自分のエラーに合うものから読んでください。
直し方1:末尾のセミコロンを消す
Section titled “直し方1:末尾のセミコロンを消す”error[E0308]: mismatched types --> src/main.rs:1:46 |1 | fn total_price(price: u32, quantity: u32) -> u32 { | ----------- ^^^ expected `u32`, found `()` | | | implicitly returns `()` as its body has no tail or `return` expression2 | price * quantity; | - help: remove this semicolon to return this valueコンパイラは「本体に末尾の式も return もないので、暗黙に () を返している」と言い、help で「この値を返すには、このセミコロンを消して」と教えてくれています。
found () と出たら、関数の最後の行を確認しましょう。Rust では、ブロックの最後にあるセミコロンなしの式が、そのブロックの戻り値になります。; を付けると文になってしまうので、関数は代わりに ()(「何もない」を表す型)を返します。セミコロンを消しましょう。
fn total_price(price: u32, quantity: u32) -> u32 { price * quantity // セミコロンなし:この値が返される}
fn main() { println!("合計: {}", total_price(250, 3));}実行結果:
合計: 750if のブロックや match の腕の中でも、同じことが起こります。詳しくは関数を見てください。
直し方2:String と &str を変換する
Section titled “直し方2:String と &str を変換する”String は、自分で所有していて伸ばすこともできる文字列です。&str は、どこかにあるテキストを借りて見ているだけのものです。くわしくは文字列を見てください。変換はどちら向きでも短く書けます。
&strからStringへ:.to_string()かString::from(...)。Stringから&strへ:&で借用する(または.as_str()を呼ぶ)。
2つ目のケースは expected &str, found String という形で出てきます。たいていは、&str を受け取る関数に String を渡したときです。
struct User { name: String,}
fn greet(name: &str) { println!("こんにちは、{name}さん!");}
fn main() { // &str -> String:.to_string() か String::from() let user = User { name: "Ferris".to_string() };
// String -> &str:& で借用する(または .as_str() を呼ぶ) greet(&user.name); greet(user.name.as_str());
let mut tags: Vec<String> = Vec::new(); tags.push(String::from("rust")); println!("{tags:?}");}実行結果:
こんにちは、Ferrisさん!こんにちは、Ferrisさん!["rust"]直し方3:数値の型を変換する
Section titled “直し方3:数値の型を変換する”error[E0308]: mismatched types --> src/main.rs:3:22 |3 | let count: i32 = names.len(); | --- ^^^^^^^^^^^ expected `i32`, found `usize` | | | expected due to thisi32 を期待していたのに usize が見つかった、というエラーです。「expected due to this」(これのせいで期待された)のラベルは、期待の元になった型注釈を指しています。
i32、i64、u32、usize はすべて別々の型で、Rust はこれらを混ぜてくれません。長さやインデックスは、いつも usize です。次のどれかで変換しましょう。
.into()(またはi64::from(x)):i32からi64のように、データが失われることが絶対にない変換のとき。i64::try_from(x)(または.try_into()):値が収まらないかもしれないとき。Resultが返るので、失敗したときどうするかを自分で決められます。as:手軽なキャストです。失敗することはありませんが、収まらない値は黙って切り詰められます。値が小さいとわかっているときだけ使いましょう。
fn average(total: i64, count: i64) -> i64 { total / count}
fn main() { let scores: Vec<i32> = vec![80, 95, 70]; let total: i32 = scores.iter().sum();
// i32 -> i64 は必ず収まるので、.into() で十分 let total: i64 = total.into();
// usize -> i64 は収まらないかもしれないので、try_from で確かめる let count = i64::try_from(scores.len()).unwrap();
println!("平均: {}", average(total, count));
// `as` でも動くが、収まらない値は黙って切り詰められる let players = scores.len() as i32; println!("プレイヤーは{players}人");}実行結果:
平均: 81プレイヤーは3人いちばん簡単なのは、型注釈のほうを変えることだったりします。let count: usize = names.len(); のようにです。さらに詳しくは型キャストと型の変換のページで説明しています。
直し方4:Some で包む、または Option から取り出す
Section titled “直し方4:Some で包む、または Option から取り出す”error[E0308]: mismatched types --> src/main.rs:3:27 |3 | let position: usize = names.iter().position(|n| *n == "Ben"); | ----- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `Option<usize>` | | | expected due to thisusize を期待していたのに Option<usize> が見つかった、というエラーです。
Option<usize> は usize ではありません。「usize があるかもしれないし、何もないかもしれない」という型です。この食い違いは、どちら向きにも起こります。
- expected
T, foundOption<T>:「何もない」場合をきちんと扱う必要があります。match、if let、unwrap_or(既定値)などを使います。 - expected
Option<T>, foundT:関数がOptionを返すので、値を包みます。10ではなくSome(10)と書きます。
fn find_discount(code: &str) -> Option<u32> { if code == "SAVE10" { return Some(10); // T -> Option<T>:Some で包む } None}
fn main() { let names = vec!["Ana", "Ben", "Cy"];
// Option<T> -> T:「見つからなかった」場合を扱う…… match names.iter().position(|n| *n == "Ben") { Some(i) => println!("Benの位置: {i}"), None => println!("Benはいません"), }
// ……または unwrap_or で既定値を選ぶ let discount: u32 = find_discount("HELLO").unwrap_or(0); println!("割引: {discount}%");}実行結果:
Benの位置: 1割引: 0%コンパイラが .expect("REASON") を提案してくることもあります。これでも動きますが、値が None のときにプログラムがクラッシュ(パニック)します。None が本当にありえないときだけ使いましょう。詳しくはOptionを見てください。
直し方5:if/else や match のすべての分岐を同じ型にそろえる
Section titled “直し方5:if/else や match のすべての分岐を同じ型にそろえる”error[E0308]: `if` and `else` have incompatible types --> src/main.rs:3:52 |3 | let label = if stock > 0 { "in stock" } else { stock }; | ---------- ^^^^^ expected `&str`, found integer | | | expected because of this「if と else の型に互換性がない」というエラーです。最初の分岐(expected because of this)のせいで &str が期待されたのに、else 側では整数が見つかった、と言っています。
if や match が値を生み出すとき、Rust はその値の型を1つに決める必要があります。そのため、すべての分岐が同じ型の値を返さなければなりません。最初の分岐で期待される型が決まり、エラーはそれと食い違う分岐を指します。match の場合は「match arms have incompatible types」(match の腕の型に互換性がない)というメッセージになります。食い違っている分岐を、ほかとそろえましょう。
fn status_text(code: u16) -> String { match code { 200 => "OK".to_string(), 404 => "Not Found".to_string(), _ => format!("不明なコード {code}"), // どの腕も String }}
fn main() { let stock = 0; let label = if stock > 0 { "在庫あり" } else { "売り切れ" }; // どちらも &str println!("{label}");
println!("{}", status_text(404)); println!("{}", status_text(418));}実行結果:
売り切れNot Found不明なコード 418ここでは1つの腕で String を作る format! が必要だったので、ほかの腕も String にそろえました。詳しくはif/elseとmatchを見てください。
よく出会う場面
Section titled “よく出会う場面”- ユーザーの入力を読むとき。
let age: u32 = input.trim();と書くと「expectedu32, found&str」になります。テキストは数値ではないからです。直し方: パースします。let age: u32 = input.trim().parse().unwrap();(またはエラーをきちんと扱います。Resultを見てください)。 Vec<String>にリテラルを push するとき。tags.push("rust")は「expectedString, found&str」になります。直し方:tags.push("rust".to_string())と書くか、中身がすべてリテラルならVec<&str>にします。- 違う数値型どうしで計算するとき。
totalがi64でnがi32のとき、total + nは E0308 になります(すぐ隣に E0277 が出ることもよくあります)。直し方: 先に片方を変換します。total + i64::from(n)のようにです。
関連するエラー
Section titled “関連するエラー”- E0277: trait bound not satisfied:トレイト境界を満たしていない。
i32にi64を足したときなど、E0308 と一緒によく出てきます。 - E0599: no method found:その型に存在しないメソッドを呼んだ。値の型が思っていたものと違うことが原因のこともよくあります。
- E0425: cannot find value in this scope:Rust がまったく知らない名前を使った。
- コンパイルエラーの一覧:エラー解説ページの全リストです。
理解度チェック
Section titled “理解度チェック”理解度チェック
1. 関数に -> i32 と書いてあるのに、「expected i32, found ()」というエラーが出ました。いちばんありそうな原因はどれですか?
2. &str を受け取る関数があり、手元には let name = String::from("Ana"); があります。どう渡せばよいですか?
3. データが黙って失われることが絶対にない変換はどれですか?
スコア: 0 / 3
さらに詳しく
Section titled “さらに詳しく”- E0308 — Rust error codes index(英語):このエラーの公式の説明です。
- 関数 — The Rust Programming Language 日本語版:「戻り値のある関数」の節で、末尾のセミコロンで関数の戻り値が変わる理由を説明しています。
- 文字列でUTF-8でエンコードされたテキストを保持する — The Rust Programming Language 日本語版:
Stringと&strの関係と、その変換方法です。 - 型変換 — Rust By Example 日本語版:
From、Into、TryFrom、TryIntoを短い例で紹介しています。 - Option — Rust 標準ライブラリ(英語):
unwrap_orやmapなど、Optionから値を取り出すためのメソッドが一覧できます。