Ошибка 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 fields8 + 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 так делает
Заголовок раздела «Почему Rust так делает»Образец должен выглядеть как значение, с которым он совпадает. В 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.
Исправление 2: Сделай вариант кортежным
Заголовок раздела «Исправление 2: Сделай вариант кортежным»Выбирай этот вариант, когда тебе действительно нужны круглые скобки: у варианта одно очевидное значение, и ты хочешь везде писать 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()). Исправления те же, что на этой странице.
Связанные ошибки
Заголовок раздела «Связанные ошибки»- E0532: expected tuple struct or tuple variant, found unit variant or struct: та же путаница для обычных структур, единичных вариантов и свободных функций, а также обратный случай (кортежный вариант без круглых скобок).
- E0533: expected unit struct, unit variant or constant, found struct variant: структурный вариант вообще без скобок, например
Shape::Circle =>. - E0026: struct or variant does not have a field named x: фигурные скобки на месте, но имя поля внутри неверное.
- E0027: pattern does not mention field x: в фигурных скобках пропущены поля; добавь
... - E0425: cannot find value in this scope: что получится, если скопировать подсказку
{ radius: _ }, но продолжать использоватьr. - Все ошибки компилятора с объяснениями
Проверь себя
Заголовок раздела «Проверь себя»Проверь себя
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
Что почитать
Заголовок раздела «Что почитать»- E0164 — Rust error codes index (на английском): официальное объяснение с примером ассоциированной константы.
- Синтаксис шаблонов — книга «Язык программирования Rust» (перевод на русский): разбор перечислений с фигурными или круглыми скобками, пропуск полей через
..и охранные условия. - Перечисления — Rust на примерах (перевод на русский): кортежные и структурные варианты в одном
match. - Patterns — The Rust Reference (на английском): какие пути могут стоять в кортежных и структурных образцах.