跳转到内容

Rust 错误 E0164:expected tuple struct or tuple variant, found struct variant(对结构体变体用了括号)

E0164 的意思是:你在模式里写了括号,比如 Shape::Circle(r),可括号前面的那个名字并不接受括号里的值。 在现在的 Rust 里,你会在三种情况下碰到它:把带命名字段的枚举变体(Circle { radius: f64 })当成元组变体来匹配;把 Celsius::new(d) 这样的关联函数当成模式;在 Volume::MAX(_) 这样的关联常量后面加了括号。每种情况都有一个简短明确的修法。(本页属于 Rust 编译错误详解系列。)

error[E0164]: expected tuple struct or tuple variant, found struct variant `Shape::Circle`
--> src/main.rs:8:9
|
8 | Shape::Circle(r) => 3.14 * r * r, // ❌ `Circle` has named fields
| ^^^^^^^^^^^^^^^^ not a tuple struct or tuple variant
|
help: the struct variant's field is being ignored
|
8 - Shape::Circle(r) => 3.14 * r * r, // ❌ `Circle` has named fields
8 + Shape::Circle { radius: _ } => 3.14 * r * r, // ❌ `Circle` has named fields
|

第一行的意思是“这里需要元组结构体或元组变体,找到的却是结构体变体 Shape::Circle”。

产生这个错误的程序是这样的:

enum Shape {
Circle { radius: f64 },
Square { side: f64 },
}
fn area(shape: &Shape) -> f64 {
match shape {
Shape::Circle(r) => 3.14 * r * r, // ❌ `Circle` has named fields
Shape::Square { side } => side * side,
}
}
fn main() {
println!("{}", area(&Shape::Square { side: 2.0 }));
}

^^^ 标出了整个模式 Shape::Circle(r),标注说明了 Rust 在这里期望的是什么:“a tuple struct or tuple variant”(元组结构体或元组变体)。模式里只有这两种东西用括号来写,因为只有它们是按位置存放值的,比如 Some(x) 或 Meters(m)。

Shape::Circle 是一个结构体变体(struct variant)。它的值有名字 radius,而命名字段要用花括号来匹配:Shape::Circle { radius }。

这里的 help: 要小心看。它建议写 Shape::Circle { radius: _ },这样模式是修好了,但半径被忽略了。我们这个分支要用到 r,所以照抄这条建议会得到一个新错误 E0425 cannot find value `r` in this scope(在当前作用域找不到值 r)。需要这个值的时候,就像修复 1 那样,在花括号里给它起个名字。

就像一张带标签格子的表格:“宽:…… 高:……”,而你在标题旁边写了“3, 4”。数字也许没错,但表格要求每个数字填进对应标签的格子里。

模式必须长得像它要匹配的值。Rust 有两种存放数据的方式,每种都有自己的模式写法(参见枚举可以携带数据):

定义方式 创建方式 匹配方式
Circle(f64)(元组变体) Shape::Circle(1.0) Shape::Circle(r)
Circle { radius: f64 }(结构体变体) Shape::Circle { radius: 1.0 } Shape::Circle { radius }

命名字段是按名字匹配的,而不是按位置,所以模式里的顺序无关紧要,以后加一个字段也不会悄悄把其他字段挤错位置。Rust 不允许混用这两种写法,这样就永远不会搞不清哪个值对应哪里。

模式还会在编译时检查,所以模式里不能运行代码。这就是为什么函数名加上 (d) 也不能当模式用,哪怕这个函数构造出来的正好是你要匹配的那种值。详见模式匹配。

当错误信息里写着 found struct variant 时选这个方法,这也是最常见的 E0164。写出字段名(Rust 会用这个名字创建一个变量),或者用 字段: 新名字 换个名字,或者用 { .. } 忽略所有字段:

enum Shape {
Circle { radius: f64 },
Rect { w: f64, h: f64 },
}
fn area(shape: &Shape) -> f64 {
match shape {
Shape::Circle { radius } => 3.14 * radius * radius, // ✅ 花括号里写字段名
Shape::Rect { w: width, h: height } => width * height, // ✅ 字段: 新名字
}
}
fn name(shape: &Shape) -> &'static str {
match shape {
Shape::Circle { .. } => "circle", // ✅ “任意一个圆”
Shape::Rect { .. } => "rectangle",
}
}
fn main() {
let shapes = [Shape::Circle { radius: 1.0 }, Shape::Rect { w: 2.0, h: 3.0 }];
for shape in &shapes {
println!("{}: {:.2}", name(shape), area(shape));
}
}

输出:

circle: 3.14
rectangle: 6.00
▶ 动手试一试

如果只列出一部分字段,就在最后加上 ..,比如 Shape::Rect { w, .. }。漏掉它会得到 E0027,字段名拼错则会得到 E0026。

当你确实想用括号时选这个方法:这个变体只有一个一看就懂的值,你更愿意到处写 Shape::Circle(1.0)。改一下定义,Circle(r) 这个模式就对了:

enum Shape {
Circle(f64), // ✅ 现在是元组变体:一个没有名字的值
Rect { w: f64, h: f64 },
}
fn area(shape: &Shape) -> f64 {
match shape {
Shape::Circle(r) => 3.14 * r * r, // ✅ 元组变体正好用括号
Shape::Rect { w, h } => w * h,
}
}
fn main() {
let small = Shape::Circle(1.0); // 创建时也用括号
let big = Shape::Circle(2.0);
let door = Shape::Rect { w: 1.0, h: 2.0 };
println!("{:.2} {:.2} {:.2}", area(&small), area(&big), area(&door));
}

输出:

3.14 12.56 2.00
▶ 动手试一试

这会改变类型本身,所以所有创建或匹配 Circle 的地方都得改成括号写法。编译器会把它们一个个列出来。如果有两个或更多的值,命名字段通常更清楚。

修复 3:匹配元组结构体本身,把函数调用放进守卫

Section titled “修复 3:匹配元组结构体本身,把函数调用放进守卫”

当错误信息里写着 found associated function、标注写着 `fn` calls are not allowed in patterns(模式里不允许调用 fn)时选这个方法。你在 Rust 需要模式的地方写了 Celsius::new(d) 这样的构造函数:

error[E0164]: expected tuple struct or tuple variant, found associated function `Celsius::new`
--> src/main.rs:11:9
|
11 | Celsius::new(d) if d > 25.0 => "warm", // ❌ `new` is a function, not a pattern
| ^^^^^^^^^^^^^^^ `fn` calls are not allowed in patterns

背后的程序:

struct Celsius(f64);
impl Celsius {
fn new(degrees: f64) -> Celsius {
Celsius(degrees)
}
}
fn feels(t: Celsius) -> &'static str {
match t {
Celsius::new(d) if d > 25.0 => "warm", // ❌ `new` is a function, not a pattern
_ => "cool",
}
}
fn main() {
println!("{}", feels(Celsius::new(30.0)));
}

Celsius 是元组结构体,所以模式就是结构体自己的名字:Celsius(d)。凡是需要调用函数或方法的判断,都放进匹配守卫(模式后面的 if ...):

struct Celsius(f64);
impl Celsius {
fn new(degrees: f64) -> Celsius {
Celsius(degrees)
}
fn is_freezing(&self) -> bool {
self.0 <= 0.0
}
}
fn feels(t: Celsius) -> &'static str {
match t {
Celsius(d) if d > 25.0 => "warm", // ✅ 元组结构体本身,再加一个守卫
Celsius(_) => "cool",
}
}
fn describe(t: &Celsius) -> &'static str {
match t {
t if t.is_freezing() => "freezing", // ✅ 方法调用放进守卫
_ => "not freezing",
}
}
fn main() {
println!("{}", feels(Celsius::new(30.0)));
println!("{}", feels(Celsius::new(12.5)));
let Celsius(d) = Celsius::new(-3.0); // ✅ 用在 `let` 里也可以
println!("{} degrees", d);
println!("{}", describe(&Celsius::new(-3.0)));
}

输出:

warm
cool
-3 degrees
freezing
▶ 动手试一试

如果这个结构体的字段是私有的(它定义在另一个模块里),你在那个模块之外就不能写 Celsius(d)。这时在守卫里用 getter 方法,比如 t if t.degrees() > 25.0。参见匹配守卫:再加一个 if。

当错误信息里写着 found associated constant 时选这个方法。常量本身就是一个完整的模式,它只匹配一个确定的值,所以括号里没有东西可放:

error[E0164]: expected tuple struct or tuple variant, found associated constant `Volume::MAX`
--> src/main.rs:10:9
|
10 | Volume::MAX(_) => "loudest", // ❌ a constant takes no ()
| ^^^^^^^^^^^^^^ not a tuple struct or tuple variant

背后的程序:

struct Volume;
impl Volume {
const MAX: u8 = 10;
}
fn label(level: u8) -> &'static str {
match level {
0 => "muted",
Volume::MAX(_) => "loudest", // ❌ a constant takes no ()
_ => "normal",
}
}
fn main() {
println!("{}", label(10));
}

单独写出常量就行:

struct Volume;
impl Volume {
const MAX: u8 = 10;
}
fn label(level: u8) -> &'static str {
match level {
0 => "muted",
Volume::MAX => "loudest", // ✅ 常量单独写
1..=3 => "quiet",
_ => "normal",
}
}
fn main() {
for level in [0, 2, 7, 10, u8::MAX] {
println!("{}: {}", level, label(level));
}
}

输出:

0: muted
2: quiet
7: normal
10: loudest
255: normal
▶ 动手试一试

标准库里的常量也一样:u32::MAX(_) 会报 E0164,u32::MAX 才是合法的模式。

把变体改成命名字段之后。 为了更清楚,你把 Move(i32, i32) 改成了 Move { x: i32, y: i32 }。之前所有的 Command::Move(x, y) => 分支现在都会报 E0164。修复:写成 Command::Move { x, y } =>。

想用“解析”函数来匹配。 比如写 match input { Version::parse(v) => ... },希望匹配时顺便运行这个函数。修复:先调用函数,再对结果做匹配:match Version::parse(input) { Some(v) => ..., None => ... }。

普通函数、普通结构体和单元变体报的是 E0532。 同样是括号用错,如果这个名字不是通过某个类型访问的,报的就是 E0532:普通函数(Some(limit()),expected a pattern, found a function call)、带命名字段的结构体(let Point(x, y) = p;,found struct `Point` ),或者单元变体(Status::Active())。修法和本页一样。

小测验

1. 对于 enum Shape { Circle { radius: f64 } },哪个分支正确地用到了半径?

2. Celsius::new(d) => ... 报了“fn calls are not allowed in patterns”。Celsius 的定义是 struct Celsius(f64);。哪个模式能用?

3. 怎样匹配关联常量 Volume::MAX?

得分: 0 / 3