Rustのエラー E0532: expected unit struct, unit variant or constant, found tuple variant(パターンの形の間違い)
E0532 は、パターンの形が、名前を書いたものの形と合っていないという意味のエラーです。よくあるのは、Circle(f64) のようにデータを持つバリアントに対して、match のアームで Shape::Circle => とだけ書いた場合です。Rust は丸かっこを付けた Shape::Circle(_) を求めています。逆の間違いも同じコードになり、そのときは「expected tuple struct or tuple variant」(タプル構造体かタプルバリアントが来るはず)という別の文面になります。Status::Active() のように、丸かっこで包むデータを持たないものに丸かっこを付けた場合です。どちらも1行で直せます。このシリーズのほかのエラーは、Rustのコンパイルエラー解説を見てください。
エラーメッセージ
Section titled “エラーメッセージ”error[E0532]: expected unit struct, unit variant or constant, found tuple variant `Shape::Circle` --> src/main.rs:8:9 |2 | Circle(f64), | ----------- `Shape::Circle` defined here...8 | Shape::Circle => "circle", // ❌ `Circle` holds a value | ^^^^^^^^^^^^^ help: use the tuple variant pattern syntax instead: `Shape::Circle(_)`「expected unit struct, unit variant or constant, found tuple variant」は「ユニット構造体、ユニットバリアント、定数のどれかが来るはずなのに、タプルバリアントが見つかった」という意味です。
このエラーが出るのは、次のプログラムです。
enum Shape { Circle(f64), Square(f64),}
fn name(shape: &Shape) -> &'static str { match shape { Shape::Circle => "circle", // ❌ `Circle` holds a value Shape::Square(_) => "square", }}
fn main() { println!("{}", name(&Shape::Circle(1.0)));}コメントは「Circle は値を持っている」という意味です(エラーになる例なので、コードはそのままにしています)。
エラーの意味
Section titled “エラーの意味”^^^ は8行目の Shape::Circle を指していて、2行目の --- はそれがどう定義されたかを示しています。Circle(f64)、つまり f64 を1つ持つタプルバリアントです。後ろに何も付けない、名前だけのパターンは、次の3つのどれかでなければいけません。
- ユニット構造体:
struct Empty;のようなもの - ユニットバリアント:
Shape::EmptyやNoneのような、データを持たないバリアント - 定数:
const LIMIT: u32 = 100;のようなもの
Shape::Circle はそのどれでもありません。help の行がすぐに直し方を教えてくれています。Shape::Circle(_)、つまり「値が何であれ、円」です。
E0532 のもう1つの見出しは、その鏡写しです。expected tuple struct or tuple variant, found unit variant `Status::Active` (タプル構造体かタプルバリアントが来るはずなのに、ユニットバリアント Status::Active が見つかった)です。found struct や found constant になることもあります。こちらは自分で名前のあとに丸かっこを書いたのに、その名前のものは丸かっこで値を受け取らない、という場合です。
申込用紙に「ペット:犬(名前:____)」と書いてあるようなものです。「犬」とだけ書くと名前の欄が空のままですし、欄のない「魚」のところに「魚(名前:バブルス)」と書くと、ありもしない欄を足したことになります。
Rust がこうする理由
Section titled “Rust がこうする理由”構造体や列挙型のバリアントは、どれも3つの形のどれかです。そして、値を作るときも、マッチするときも、同じ形で書きます(列挙型はデータを持てるを見てください)。
| 種類 | 作るとき | マッチするとき |
|---|---|---|
ユニット:Empty |
Shape::Empty |
Shape::Empty |
タプル:Circle(f64) |
Shape::Circle(1.0) |
Shape::Circle(r) または Shape::Circle(..) |
構造体:Rect { w: f64, h: f64 } |
Shape::Rect { w: 2.0, h: 3.0 } |
Shape::Rect { w, h } または Shape::Rect { .. } |
形のルールが厳しいので、パターンはいつもマッチする値と同じ見た目になります。match を読めば、それぞれのアームがどんなデータを持っているかがわかります。型を変えたときにも役立ちます。あとで Empty が値を持つようになって Empty(u32) に変わったら、古い Shape::Empty のアームがすべてエラーとして示されます。黙って別のものにマッチしてしまうことはありません。
直し方1:(_)、(..)、または値の名前を付ける
Section titled “直し方1:(_)、(..)、または値の名前を付ける”メッセージが found tuple variant や found tuple struct のときは、これを選びます。いちばんよくある E0532 です。いらない値1つにつき _、まとめて全部飛ばすなら ..、値を使いたいなら名前を書きます。
enum Shape { Circle(f64), Rect(f64, f64), Empty,}
fn name(shape: &Shape) -> &'static str { match shape { Shape::Circle(_) => "円", // ✅ フィールド1つ、無視する Shape::Rect(..) => "長方形", // ✅ フィールドがいくつでも、全部無視する Shape::Empty => "なし", // ユニットバリアント:丸かっこなし }}
fn area(shape: &Shape) -> f64 { match shape { Shape::Circle(r) => 3.14 * r * r, // ✅ 値が必要なので、フィールドに名前を付ける Shape::Rect(w, h) => w * h, Shape::Empty => 0.0, }}
fn main() { let shapes = [Shape::Circle(1.0), Shape::Rect(2.0, 3.0), Shape::Empty]; for shape in &shapes { println!("{}: {:.2}", name(shape), area(shape)); } let circles = shapes.iter().filter(|s| matches!(s, Shape::Circle(_))).count(); println!("円は {} 個", circles);}実行結果:
円: 3.14長方形: 6.00なし: 0.00円は 1 個(..) は値をいくつも持つバリアントで便利です。あとで値を1つ足しても、そのまま動きます。_ と .. で一部を無視するを見てください。
直し方2:ユニットバリアントの () を消す
Section titled “直し方2:ユニットバリアントの () を消す”メッセージが found unit variant や found unit struct のときは、これを選びます。そのバリアントはデータを持たないので、丸かっこの中に入れるものがありません。
error[E0532]: expected tuple struct or tuple variant, found unit variant `Status::Active` --> src/main.rs:8:9 |2 | Active, | ------ `Status::Active` defined here...8 | Status::Active() => "active", // ❌ `Active` holds no data | ^^^^^^^^^^^^^^^^ help: use this syntax instead: `Status::Active`エラーになるプログラムはこちらです(コメントは「Active はデータを持たない」という意味です)。
enum Status { Active, Banned(String),}
fn label(s: &Status) -> &'static str { match s { Status::Active() => "active", // ❌ `Active` holds no data Status::Banned(_) => "banned", }}
fn main() { println!("{}", label(&Status::Active));}パターンでも、値を作るところでも、名前だけを書きます。
#[derive(Debug)]enum Status { Active, Banned(String),}
fn label(s: &Status) -> String { match s { Status::Active => String::from("有効"), // ✅ ユニットバリアント:() なし Status::Banned(reason) => format!("停止中({})", reason), }}
fn main() { println!("{}", label(&Status::Active)); println!("{}", label(&Status::Banned(String::from("スパム")))); let s = Status::Active; // ✅ 作るときも () なし println!("{:?}", s);}実行結果:
有効停止中(スパム)Activeもしそのバリアントにデータを持たせるつもりだったのなら、定義のほうを変えます。たとえば「いつから」のタイムスタンプを持つ Active(u64) にすれば、Status::Active(_) が正しいパターンになります。
直し方3:名前付きフィールドの構造体には波かっこを使う
Section titled “直し方3:名前付きフィールドの構造体には波かっこを使う”メッセージが found struct のときは、これを選びます。struct Point { x: i32, y: i32 } のような名前付きフィールドを持つ構造体は、丸かっこではなく、波かっことフィールド名で分解します。
error[E0532]: expected tuple struct or tuple variant, found struct `Point` --> src/main.rs:8:9 |1 | / struct Point {2 | | x: i32,3 | | y: i32,4 | | } | |_- `Point` defined here...8 | let Point(x, y) = p; // ❌ `Point` has named fields | ^^^^^^^^^^^ help: use struct pattern syntax instead: `Point { x, y }`エラーになるプログラムはこちらです(コメントは「Point は名前付きフィールドを持っている」という意味です)。
struct Point { x: i32, y: i32,}
fn main() { let p = Point { x: 3, y: 4 }; let Point(x, y) = p; // ❌ `Point` has named fields println!("{} {}", x, y);}rustc が提案している Point { x, y } が、まさに正解です。別の変数名にしたいときは フィールド: 名前 と書きます。
struct Point { x: i32, y: i32,}
struct Meters(f64); // タプル構造体:こちらは丸かっこが正しい
fn main() { let p = Point { x: 3, y: 4 }; let Point { x, y } = p; // ✅ 波かっことフィールド名 println!("x = {}, y = {}", x, y);
let q = Point { x: -1, y: 7 }; let Point { x: left, y: up } = q; // ✅ 分解しながら名前を変える println!("left = {}, up = {}", left, up);
let Meters(m) = Meters(2.5); // ✅ タプル構造体には、タプルのパターン println!("{} m", m);}実行結果:
x = 3, y = 4left = -1, up = 72.5 m名前付きフィールドを持つ列挙型のバリアントで同じ間違いをすると(Rect { w: f64, h: f64 } に Shape::Rect(w, h) と書くなど)、別のコード E0164 になります。直し方は同じで、波かっこを使います。
直し方4:関数呼び出しの代わりに、定数かマッチガードを使う
Section titled “直し方4:関数呼び出しの代わりに、定数かマッチガードを使う”メッセージが expected a pattern, found a function call(パターンが来るはずなのに、関数呼び出しが見つかった)のときは、これを選びます。パターンはコンパイル時にわかっていなければいけませんが、関数呼び出しはそうではありません。
error[E0532]: expected a pattern, found a function call --> src/main.rs:7:14 |7 | Some(limit()) => "full", // ❌ a function call can't be a pattern | ^^^^^ not a tuple struct or tuple variant | = note: function calls are not allowed in patterns: <https://doc.rust-lang.org/book/ch19-00-patterns.html>note: は「パターンの中で関数を呼ぶことはできない」という意味です。エラーになるプログラムはこちらです(コメントは「関数呼び出しはパターンにできない」という意味です)。
fn limit() -> u32 { 100}
fn status(users: Option<u32>) -> &'static str { match users { Some(limit()) => "full", // ❌ a function call can't be a pattern Some(_) => "open", None => "closed", }}
fn main() { println!("{}", status(Some(100)));}値がずっと変わらないなら、const にします。定数はパターンの中で使えます。計算しないといけない値なら、値に名前を付けて、マッチガードで比べます。
const LIMIT: u32 = 100; // 定数はパターンの中で使える
fn limit_for(premium: bool) -> u32 { if premium { 500 } else { 100 }}
fn status(users: Option<u32>) -> &'static str { match users { Some(LIMIT) => "満員", // ✅ 定数 Some(_) => "空きあり", None => "受付停止", }}
fn status_for(users: Option<u32>, premium: bool) -> &'static str { match users { Some(n) if n >= limit_for(premium) => "満員", // ✅ 関数はガードの中で呼ぶ Some(_) => "空きあり", None => "受付停止", }}
fn main() { println!("{}", status(Some(100))); println!("{}", status(Some(42))); println!("{}", status(None)); println!("{}", status_for(Some(100), false)); println!("{}", status_for(Some(100), true));}実行結果:
満員空きあり受付停止満員空きありマッチガードについては、マッチガード:if を付け足すで説明しています。
よく出会う場面
Section titled “よく出会う場面”ユニットバリアントに値を持たせたあと。 Event::Logout はユニットバリアントでしたが、それを Logout(u64) に変えました。すると Event::Logout => のアームがすべて E0532 になります。直し方:Event::Logout(_) =>、値が必要なら Event::Logout(at) => です。
値の種類だけを調べたいとき。 Java や TypeScript から来た人は、「円かどうか」を調べるつもりで if let Shape::Circle = shape と書くことがあります。if let Shape::Circle(_) = shape と書くか、手軽に bool がほしいなら matches!(shape, Shape::Circle(_)) と書きます。
Shape:: を付けないと E0530 になる。 use Shape::*; のあとで Ok => や Circle => のように名前だけを書くと、rustc はそれを新しい変数だと読みます。そして E0530 match bindings cannot shadow tuple variants(match の束縛でタプルバリアントを隠すことはできない)を出し、try specify the pattern arguments: `Ok(..)` (パターンの引数を指定してみてください)という help を表示します。直し方は同じで、丸かっこを付けます。
関連するエラー
Section titled “関連するエラー”- E0533: expected unit struct, unit variant or constant, found struct variant:名前付きフィールドを持つバリアントで同じ間違いをしたとき。
{ .. }を付けます。 - E0164: expected tuple struct or tuple variant, found struct variant:名前付きフィールドを持つバリアントや、関連定数・関連関数に丸かっこを付けたとき。
- E0023: wrong number of fields in a tuple pattern(英語):丸かっこは正しいけれど、書いた名前の数が多すぎるか少なすぎる。
- E0026: struct or variant does not have a field named x:パターンの波かっこの中のフィールド名が間違っている。
- E0004: non-exhaustive patterns:アームを直したあとも、
matchはすべてのバリアントを扱わなければいけません。 - コンパイルエラーの一覧:エラー解説ページの全リストです。
理解度チェック
Section titled “理解度チェック”理解度チェック
1. enum Shape { Circle(f64), Empty } で、コンパイルできる match のアームはどれですか?
2. rustc に「expected tuple struct or tuple variant, found unit variant Status::Active」と言われました。どう直しますか?
3. struct Point { x: i32, y: i32 } を let で分解するには、どう書きますか?
スコア: 0 / 3
さらに詳しく
Section titled “さらに詳しく”- E0532 — Rust error codes index(英語):このエラーの公式の説明です。
- パターン記法 — The Rust Programming Language 日本語版:構造体や列挙型の分解、
_と..で値を無視する方法、マッチガードです。 - 列挙型 — Rust By Example 日本語版:ユニットバリアントとタプルバリアントのマッチを並べて見られます。
- 構造体 — Rust By Example 日本語版:名前付きフィールドには波かっこ、タプル構造体には丸かっこを使う分解です。
- Patterns — The Rust Reference(英語):パスパターン、タプル構造体パターン、構造体パターンの正確なルールです。