Что требует Rust от шаблона параметра функции, чтобы вызов всегда был допустим?

Что требует Rust от шаблона параметра функции, чтобы вызов всегда был допустим?

Проходите собеседования с ИИ помощником Hintsage

Краткий ответ

Шаблон параметра функции должен быть неопровержимым: он обязан совпадать с любым значением объявленного типа. Rust запрещает шаблоны, которые могут не совпасть, поскольку у обычного вызова функции нет механизма обработать отказ сопоставления.

Деструктуризация допустима, если она гарантированно успешна. Например, кортежный или структурный шаблон может извлекать поля, но шаблон конкретного варианта Option или Result для параметра функции недопустим.

Исторический контекст

Шаблоны в Rust используются не только в match, но и при связывании параметров, локальных переменных и значений. Единый синтаксис позволяет сразу разбирать составные значения и связывать их части вместо ручного обращения к каждому полю.

При этом Rust разделяет неопровержимые шаблоны, которые подходят всегда, и опровержимые, которые могут не совпасть. Для функций выбран только первый вид, потому что обычная сигнатура функции описывает успешный вызов без дополнительного результата или ветви отказа.

Постановка проблемы

Если разрешить в параметре функции шаблон, совпадающий лишь с частью значений, вызов функции мог бы не иметь определённого поведения для допустимого значения аргумента. Например, функция, принимающая Option, не может принять только Some и молча решить, что делать с None.

Такое ограничение защищает от скрытого отказа на границе функции. Для выборочной обработки нужно явно использовать match, if let или другой механизм, в котором ветвь несовпадения видна в коде.

Подробное решение

Неопровержимый шаблон совпадает с любым значением своего типа. Именованная переменная, _, а также корректная деструктуризация кортежа или структуры являются примерами таких шаблонов.

Опровержимый шаблон допускает несовпадение. К нему относятся шаблоны конкретных вариантов перечисления, диапазоны, некоторые литералы и условия с дополнительным ограничением. Они подходят для match и if let, но не для обычного параметра функции.

enum Packet { Data(u8), Empty, } fn inspect(packet: Packet) { match packet { Packet::Data(value) => println!("{value}"), Packet::Empty => println!("empty"), } } // Такой параметр запрещён: шаблон не совпадает с Packet::Empty. // fn only_data(Packet::Data(value): Packet) {}

В примере параметр packet принимает любое значение Packet, а выбор варианта выполняется внутри match. Это делает обработку всех случаев явной и позволяет компилятору проверить полноту сопоставления.

Деструктуризация сама по себе не делает шаблон опровержимым. Параметр-кортеж гарантированно имеет нужное число элементов по своему типу, поэтому его части можно безопасно связать. Ограничение относится не к сложности шаблона, а к возможности несовпадения.

Ситуация из практики

Предположим, обработчик получает Option<Request>, но бизнес-логика применима только к Some. Вариант с попыткой принять в параметре только Some не компилируется: он скрывал бы обработку None и не задавал бы контракт функции для всего типа аргумента.

Первый рабочий вариант — принять Option<Request> и выполнить match внутри функции. Его плюс — полный контроль над обоими случаями; минус — функция содержит логику, которая иногда может быть нужна вызывающему коду.

Второй вариант — сначала извлечь Request у вызывающего кода, а отдельной функции передавать уже Request. Это делает контракт обработчика проще, но переносит решение о поведении при None на вызывающий код.

Обычно выбирают второй вариант для функции, которая действительно работает только с существующим запросом, и первый — если функция отвечает за политику обработки отсутствующего значения. В обоих случаях отказ от опровержимого шаблона в параметре сохраняет явный контроль над всеми входами.

Что кандидаты часто упускают

  1. Вопрос: Может ли параметр-кортежный шаблон быть неопровержимым?

    Ответ: Да. Если тип параметра — кортеж фиксированной структуры, например кортеж из двух целых чисел, он всегда имеет эту структуру. Поэтому разбор кортежа на два связанных имени не создаёт возможности несовпадения и допустим.

  2. Вопрос: Чем обработка опровержимого шаблона в match отличается от его использования в параметре функции?

    Ответ: В match каждая ветвь является частью явного выбора, а компилятор проверяет, покрыты ли возможные значения. В параметре функции ветвей выбора нет: функция должна получить управление при любом значении объявленного типа. Поэтому опровержимый шаблон там отвергается ещё на этапе компиляции.

  3. Вопрос: Можно ли использовать шаблон конкретного варианта перечисления при объявлении локальной переменной?

    Ответ: Обычный let также требует неопровержимый шаблон, поскольку присваиваемое значение может не совпасть. Для возможного несовпадения применяют match, if let или let-else; эти конструкции явно задают поведение для случая, когда шаблон не подошёл. Поэтому запрет в параметрах функции является частью более общего правила о местах, где Rust не допускает неявный отказ сопоставления.