Перейти к содержимому

Ошибка 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». help предлагает образец с фигурными скобками, в котором поле пропускается.

Вот программа, которая её вызывает (комментарий // ❌ оставлен на английском, потому что rustc цитирует эту строку; он значит «у 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. Когда значение нужно, дай ему имя внутри фигурных скобок, как в исправлении 1.

Это как бланк с подписанными полями «Ширина: … Высота: …», а ты написал «3, 4» рядом с заголовком. Числа, может, и верные, но бланк хочет видеть каждое в своём подписанном поле.

Образец должен выглядеть как значение, с которым он совпадает. В Rust есть два способа хранить данные, и у каждого свой образец (см. урок Перечисления):

Определён как Создаётся через Сопоставляется через
Circle(f64) (кортежный вариант) Shape::Circle(1.0) Shape::Circle(r)
Circle { radius: f64 } (структурный вариант) Shape::Circle { radius: 1.0 } Shape::Circle { radius }

Именованные поля сопоставляются по имени, а не по позиции, поэтому порядок в образце не важен, а новое поле, добавленное позже, не сдвигает молча остальные. Смешивать два стиля нельзя, так что вопроса, какое значение куда идёт, никогда не возникает.

Кроме того, образцы проверяются на этапе компиляции, поэтому не могут выполнять код. Вот почему имя функции с (d) после него тоже не может быть образцом, даже если функция создаёт ровно то значение, которое ты сопоставляешь. Подробнее — в уроке Сопоставление с образцом.

Исправление 1: Используй фигурные скобки и имена полей

Заголовок раздела «Исправление 1: Используй фигурные скобки и имена полей»

Выбирай этот вариант, когда в сообщении написано 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 { .. } => "круг", // ✅ «любой круг»
Shape::Rect { .. } => "прямоугольник",
}
}
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));
}
}

Вывод:

круг: 3.14
прямоугольник: 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: Сопоставляй саму кортежную структуру, а функции вызывай в охранном условии

Заголовок раздела «Исправление 3: Сопоставляй саму кортежную структуру, а функции вызывай в охранном условии»

Выбирай этот вариант, когда в сообщении написано found associated function, а в надписи — `fn` calls are not allowed in patterns («вызовы fn в образцах запрещены»). Ты написал конструктор вроде Celsius::new(d) там, где Rust нужен образец:

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

Программа, которая к этому приводит (комментарий значит «new — функция, а не образец»):

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). Всё, что требует вызова функции или метода, уходит в охранное условие (match guard, 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 => "тепло", // ✅ сама кортежная структура плюс охранное условие
Celsius(_) => "прохладно",
}
}
fn describe(t: &Celsius) -> &'static str {
match t {
t if t.is_freezing() => "мороз", // ✅ методы — в охранное условие
_ => "не мороз",
}
}
fn main() {
println!("{}", feels(Celsius::new(30.0)));
println!("{}", feels(Celsius::new(12.5)));
let Celsius(d) = Celsius::new(-3.0); // ✅ работает и с `let`
println!("{} градуса", d);
println!("{}", describe(&Celsius::new(-3.0)));
}

Вывод:

тепло
прохладно
-3 градуса
мороз
▶ Попробуй сам

Если поле структуры приватное (она живёт в другом модуле), снаружи этого модуля написать Celsius(d) нельзя. Тогда используй геттер в охранном условии, например t if t.degrees() > 25.0. Подробнее — в уроке Сопоставление с образцом.

Исправление 4: Убери круглые скобки после константы

Заголовок раздела «Исправление 4: Убери круглые скобки после константы»

Выбирай этот вариант, когда в сообщении написано 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 => "без звука",
Volume::MAX => "на максимум", // ✅ константа сама по себе
1..=3 => "тихо",
_ => "обычно",
}
}
fn main() {
for level in [0, 2, 7, 10, u8::MAX] {
println!("{}: {}", level, label(level));
}
}

Вывод:

0: без звука
2: тихо
7: обычно
10: на максимум
255: обычно
▶ Попробуй сам

То же с константами из стандартной библиотеки: 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