コンテンツにスキップ

Rustのエラー E0533: expected unit struct, unit variant or constant, found struct variant({ } の書き忘れ)

E0533 は、Shape::Circle のような名前を単独で書いたけれど、その場所では名前だけでは足りない、という意味のエラーです。いちばん多いのは、名前付きのフィールドを持つ列挙型のバリアント(Circle { radius: f64 })を、{ } なしで書いた場合です。match のアームでも、値を作るときでも起こります。もう少しまれなのは、決まった値しか置けない場所に関数やメソッドを書いた場合です。rustc が何を求めているのかがわかれば、どちらもすぐに直せます。このシリーズのほかのエラーは、Rustのコンパイルエラー解説を見てください。

error[E0533]: expected unit struct, unit variant or constant, found struct variant `Shape::Circle`
--> src/main.rs:8:9
|
8 | Shape::Circle => "circle", // ❌ `Circle` has a field
| ^^^^^^^^^^^^^ not a unit struct, unit variant or constant
|
help: the struct variant's field is being ignored
|
8 | Shape::Circle { radius: _ } => "circle", // ❌ `Circle` has a field
| +++++++++++++

「expected unit struct, unit variant or constant, found struct variant Shape::Circle」は「ユニット構造体、ユニットバリアント、定数のどれかが来るはずなのに、構造体バリアント Shape::Circle が見つかった」という意味です。

このエラーが出るのは、次のプログラムです。

enum Shape {
Circle { radius: f64 },
Square { side: f64 },
}
fn name(shape: &Shape) -> &'static str {
match shape {
Shape::Circle => "circle", // ❌ `Circle` has a field
Shape::Square { .. } => "square",
}
}
fn main() {
println!("{}", name(&Shape::Square { side: 2.0 }));
}

コメントは「Circle にはフィールドがある」という意味です(エラーになる例なので、コードはそのままにしています)。

^^^ は Shape::Circle を指していて、見出しの行は、そこに何なら置けたのかを説明しています。名前だけのパターンは、次の3つのどれかでなければなりません。

  • ユニット構造体(unit struct):struct Empty; のようなもの
  • ユニットバリアント(unit variant):データを持たないバリアント。Shape::Empty や None など
  • 定数(constant):const MAX: u32 = 100; のようなもの

Shape::Circle はそのどれでもありません。これは構造体バリアント(struct variant)で、radius を持っています。Shape::Circle とだけ書くと「円」にはマッチしますが、そのフィールドについては何も言っていません。Rust は、パターンの形がバリアントの形と合っていることを求めます。help: の行(「構造体バリアントのフィールドを無視している」)が直し方を見せてくれています。波かっこを付けるのです。

E0533 にはもう1つの種類があり、expected value, found struct variant(値が来るはずなのに、構造体バリアントが見つかった)と表示されます。同じ取り違えですが、値を作るときのもので、たいてい Shape::Circle(2.0) のように丸かっこで書いてしまった場合です。

宅配便の伝票に「サイズ:S、M、L」という欄があって、そこに「特注」と書いたようなものです。特注も本当にある選択肢ですが、その隣に寸法を書き込む必要があります。

列挙型のバリアントには3つの形があり、作るときもマッチするときも、それぞれ決まった書き方をします(列挙型はデータを持てるを見てください)。

バリアント 作るとき マッチするとき
ユニット:Empty Shape::Empty Shape::Empty
タプル:Point(f64, f64) Shape::Point(1.0, 2.0) Shape::Point(x, y) または Shape::Point(..)
構造体:Circle { radius: f64 } Shape::Circle { radius: 2.0 } Shape::Circle { radius } または Shape::Circle { .. }

形を厳しく守ることで、パターンはいつもマッチする相手と同じ見た目になります。だから match を読めば、それぞれのアームがどんなデータを持っているかが正確にわかります。それに、バリアントの形が変わったとき(たとえば Empty にフィールドが増えたとき)、古い形を使っている場所がすべて指摘されるので、黙って間違った動きをすることがありません。

直し方1:パターンに { .. } かフィールドを付ける

Section titled “直し方1:パターンに { .. } かフィールドを付ける”

エラーが match、if let、matches! のアームを指していて、found struct variant と言っているときは、これを選びます。フィールドが必要なければ { .. } と書き、必要なら名前を書きます。rustc の提案した { radius: _ } でも動きますが、{ .. } のほうが同じことを短く言えます。

enum Shape {
Circle { radius: f64 },
Square { side: f64 },
Empty,
}
fn name(shape: &Shape) -> &'static str {
match shape {
Shape::Circle { .. } => "円", // ✅ 「半径がいくつでも、円」
Shape::Square { .. } => "正方形",
Shape::Empty => "なし", // ユニットバリアント:波かっこはいらない
}
}
fn area(shape: &Shape) -> f64 {
match shape {
Shape::Circle { radius } => 3.14 * radius * radius, // ✅ 必要なのでフィールド名を書く
Shape::Square { side } => side * side,
Shape::Empty => 0.0,
}
}
fn main() {
let shapes = [Shape::Circle { radius: 1.0 }, Shape::Square { side: 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
正方形: 9.00
なし: 0.00
円は1個
▶ 実際に試してみよう

{ .. } は、フィールドがいくつあっても、どの構造体バリアントにも使えます。タプルバリアントでは、同じことを (..) と書きます。詳しくは_ と .. で一部を無視するを見てください。

直し方2:波かっことフィールド名で値を作る

Section titled “直し方2:波かっことフィールド名で値を作る”

メッセージが expected value, found struct variant のときは、これを選びます。構造体バリアントを、タプルバリアントのように(あるいはフィールドなしで)作ろうとしています。

error[E0533]: expected value, found struct variant `Shape::Circle`
--> src/main.rs:8:17
|
8 | let shape = Shape::Circle(2.0); // ❌ `Circle` has a named field
| ^^^^^^^^^^^^^ not a value
|
help: you might have meant to create a new value of the struct
|
8 - let shape = Shape::Circle(2.0); // ❌ `Circle` has a named field
8 + let shape = Shape::Circle { radius: /* value */ }; // ❌ `Circle` has a named field
|

help は「構造体の新しい値を作るつもりだったのかもしれない」という意味です。

このエラーが出るプログラムです(コメントは「Circle には名前付きのフィールドがある」という意味です)。

#[derive(Debug)]
enum Shape {
Circle { radius: f64 },
Square { side: f64 },
}
fn main() {
let shape = Shape::Circle(2.0); // ❌ `Circle` has a named field
println!("{:?}", shape);
}

提案の中の /* value */ は、あなたの値を入れる場所の目印です。構造体バリアントのフィールドは、波かっこの中で名前を付けて設定します。

#[derive(Debug)]
enum Shape {
Circle { radius: f64 },
Square { side: f64 },
}
impl Shape {
// `Shape::circle(2.0)` と書きたいなら、短いコンストラクタを用意する
fn circle(radius: f64) -> Shape {
Shape::Circle { radius }
}
}
fn main() {
let a = Shape::Circle { radius: 2.0 }; // ✅ 波かっことフィールド名
let b = Shape::Square { side: 1.5 };
let c = Shape::circle(0.5);
println!("{:?}", a);
println!("{:?}", b);
println!("{:?}", c);
}

実行結果:

Circle { radius: 2.0 }
Square { side: 1.5 }
Circle { radius: 0.5 }
▶ 実際に試してみよう

どうしても Shape::Circle(2.0) と書きたいなら、定義をタプルバリアントの Circle(f64) に変えましょう。フィールドが1つで意味がはっきりしているなら、それも良い選択です。フィールドがいくつもあるときは、名前付きのほうがわかりやすくなります。

直し方3:関数の代わりに定数かマッチガードを使う

Section titled “直し方3:関数の代わりに定数かマッチガードを使う”

メッセージが found associated function(関連関数が見つかった)のときは、これを選びます。String::len のようなメソッドでも出ます。パターンは、コンパイルのときにわかっている値でなければなりません。関数の呼び出しはそうではないので、関数の名前をパターンとして使うことはできません。

error[E0533]: expected unit struct, unit variant or constant, found associated function `Limits::max_users`
--> src/main.rs:12:9
|
12 | Limits::max_users => "full", // ❌ a function can't be a pattern
| ^^^^^^^^^^^^^^^^^ not a unit struct, unit variant or constant

このエラーが出るプログラムです(コメントは「関数はパターンになれない」という意味です)。

struct Limits;
impl Limits {
fn max_users() -> u32 {
100
}
}
fn status(users: u32) -> &'static str {
match users {
0 => "empty",
Limits::max_users => "full", // ❌ a function can't be a pattern
_ => "open",
}
}
fn main() {
println!("{}", status(100));
}

値がずっと変わらないなら、const にしましょう。定数はパターンになれます。計算が必要なら、関数はそのままにして、if を使ったマッチガードで比べます。

struct Limits;
impl Limits {
const MAX_USERS: u32 = 100; // 定数はパターンとして使える
fn max_rooms(premium: bool) -> u32 {
if premium { 20 } else { 5 }
}
}
fn status(users: u32) -> &'static str {
match users {
0 => "空っぽ",
Limits::MAX_USERS => "満員", // ✅ 定数
_ => "受付中",
}
}
fn rooms_status(rooms: u32, premium: bool) -> &'static str {
match rooms {
n if n >= Limits::max_rooms(premium) => "これ以上部屋を増やせません", // ✅ ガードの中で呼ぶ
_ => "部屋を追加できます",
}
}
fn main() {
println!("{}", status(0));
println!("{}", status(100));
println!("{}", status(42));
println!("{}", rooms_status(5, false));
println!("{}", rooms_status(5, true));
}

実行結果:

空っぽ
満員
受付中
これ以上部屋を増やせません
部屋を追加できます
▶ 実際に試してみよう

マッチガードについては、マッチガード:if を付け足すで説明しています。

ユニットバリアントにフィールドを追加したあと。 もともとユニットバリアントだった Status::Done を、Done { at: u64 } に変えたとします。すると、Status::Done => というアームも、let s = Status::Done; という行も、すべて E0533 になります。直し方:パターンでは Status::Done { .. } =>、作る場所では(本当の値を入れて)Status::Done { at: 0 } と書きます。

ほかの言語から来た場合。 Java や TypeScript では、値の種類だけを調べるために case Circle: と書くことがあります。Rust では Shape::Circle { .. } => と書きます。ちょっと bool が欲しいだけなら matches!(shape, Shape::Circle { .. }) です。

タプルバリアント版。 Circle(f64) のようなタプルバリアントに Shape::Circle => とだけ書いたアームは、別のコード E0532 になります。見出しは同じ「expected unit struct, unit variant or constant, found tuple variant Shape::Circle」(ただし最後が「タプルバリアントが見つかった」)です。直し方は Shape::Circle(_) か Shape::Circle(..) です。逆に、Circle { radius: f64 } に対して Shape::Circle(r) => のように、パターンで構造体バリアントに丸かっこを使うと、E0164「expected tuple struct or tuple variant, found struct variant」(タプル構造体かタプルバリアントが来るはずなのに、構造体バリアントが見つかった)になります。直し方:Shape::Circle { radius: r } =>。

理解度チェック

1. enum Shape { Circle { radius: f64 } } に対して、コンパイルできる match のアームはどれですか?

2. let s = Shape::Circle(2.0); で「expected value, found struct variant」が出ました。直し方はどれですか?

3. 単独で match のパターンとして使えるのはどれですか?

スコア: 0 / 3