В чём состоит семантическая разница между выражением с точкой с запятой и тем же выражением без неё в Rust?
В Rust выражение без точки с запятой может быть хвостовым выражением блока и передать его значение наружу. Точка с запятой превращает выражение в оператор: его результат отбрасывается, а блок в таком месте обычно получает значение типа ().
Это влияет не только на стиль записи, но и на тип блока, возвращаемое значение функции и совместимость ветвей if или match.
Rust использует выраженческую модель, в которой блоки, условные конструкции и сопоставление могут вычислять значение. Такой подход позволяет составлять сложные выражения из небольших блоков без отдельного синтаксиса для каждого случая.
Точка с запятой нужна для явного отделения вычисления от возвращаемого значения блока. Она позволяет выполнить выражение ради побочного эффекта и не использовать его результат как значение окружающего выражения.
Ошибка часто возникает, когда разработчик добавляет точку с запятой после последнего выражения функции или блока. Код продолжает выглядеть логично, но функция начинает возвращать (), что приводит к ошибке несоответствия типов или к незаметному отбрасыванию результата.
Особенно важно различать промежуточные выражения с побочными эффектами и последнее выражение блока. Первым обычно нужна точка с запятой, второе без неё становится результатом блока.
В блоке Rust последняя строка без точки с запятой является хвостовым выражением. Значение и тип этого выражения становятся значением и типом всего блока.
Если после выражения стоит точка с запятой, оно рассматривается как оператор. Его вычисление всё ещё происходит, поэтому побочные эффекты сохраняются, но результат не передаётся дальше. Если это последнее выражение блока, блок получает значение ().
В square выражение x * x без точки с запятой возвращается из функции. В discarded результат x * x отбрасывается, а неявный результат функции имеет тип ().
Точка с запятой не означает, что вычисление вообще не выполняется. Например, вызов функции с побочным эффектом будет выполнен, но его возвращаемое значение не станет результатом блока.
У условных конструкций ветви должны согласовываться по типу результата. Поэтому добавление точки с запятой только в одной ветви может сделать типы ветвей несовместимыми: одна ветвь даст (), а другая — исходное значение.
Есть важное ограничение: точка с запятой не меняет тип самого значения внутри выражения и не делает произвольное значение равным (). Она меняет роль выражения в окружающем блоке — из возвращаемого результата в оператор с отброшенным результатом.
В обработчике запроса нужно вычислить статус и вернуть его вызывающему коду. Разработчик добавляет точку с запятой после вызова вычисляющей функции, потому что привык завершать каждую строку оператором. В результате обработчик возвращает (), а код маршрутизации не может получить ожидаемый статус.
Рассматривались два варианта. Можно было явно написать return — это делает намерение очевидным, но при коротком блоке добавляет синтаксический шум. Можно было оставить вызов последним выражением без точки с запятой — это соответствует идиоматичному стилю Rust, но требует понимания семантики хвостового выражения.
Выбран второй вариант для простого обработчика, а явный return оставлен для досрочного выхода из нескольких ветвей. Это сохраняет корректный тип результата и одновременно делает сложный поток управления понятнее.
i32 в ()?Нет, она не меняет тип вычисленного значения как такового. Выражение всё равно вычисляется со своим исходным типом, но его результат отбрасывается как у оператора; значение окружающего блока в этой позиции становится ().
Потому что только последнее выражение без точки с запятой является хвостовым выражением блока. Предыдущие выражения должны быть операторами или объявлениями, поэтому их результаты не формируют значение блока, даже если они сами имеют ненулевой тип.
Первая ветвь обычно имеет результат (), а вторая сохраняет тип своего хвостового выражения. Если эти типы не совпадают, компилятор сообщит о несовместимости типов; автоматическое приведение произвольного значения к () не выполняется. Чтобы исправить код, нужно либо отбросить результат в обеих ветвях, либо вернуть совместимые значения из обеих.