コンテンツにスキップ

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のコンパイルエラー解説を見てください。

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 は値を持っている」という意味です(エラーになる例なので、コードはそのままにしています)。

^^^ は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 になることもあります。こちらは自分で名前のあとに丸かっこを書いたのに、その名前のものは丸かっこで値を受け取らない、という場合です。

申込用紙に「ペット:犬(名前:____)」と書いてあるようなものです。「犬」とだけ書くと名前の欄が空のままですし、欄のない「魚」のところに「魚(名前:バブルス)」と書くと、ありもしない欄を足したことになります。

構造体や列挙型のバリアントは、どれも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 = 4
left = -1, up = 7
2.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 を付け足すで説明しています。

ユニットバリアントに値を持たせたあと。 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 を表示します。直し方は同じで、丸かっこを付けます。

理解度チェック

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