Как статически утверждать, что конец функции недоступен - PullRequest
0 голосов
/ 16 июня 2020

У меня есть довольно сложный оператор match (с вложенными if s и т.п.) в конце функции. Каждая ветвь этого должна либо явно возвращаться из функции, либо вызывать некоторую -> ! функцию (например, process::exit).

Как для общения с другими программистами, так и для защиты себя от себя я бы хотел бы сказать компилятору, чтобы он утверждал, что что-либо после этого match недоступно. Я знаю, что он умеет делать это статически, поскольку, если я помещаю туда код, я получаю предупреждения во время компиляции.

Две вещи, которые я пробовал:

  1. Назначьте match выписка на let _: ! = match .... Однако ! все еще экспериментальный, так что это не работает.

  2. Оберните match в закрывающую move || -> ! { match ... }();. Однако это ограничивает меня от возможности использовать только return из родительской функции.


Specifi c детали моей ситуации, которые не обязательно применимы в целом:

  • Рассматриваемая функция fn main() -> ()
  • Условное логарифмическое c должно либо отклоняться от функции возврата (), либо отклоняться от функции возврата !
  • Невыполнение этого требования указывает путь, по которому ошибка либо не обрабатывается правильно, либо сообщается
  • return -ing функции из условного журнала c должны быть там, чтобы использовать значения развернутые спичками

1 Ответ

4 голосов
/ 16 июня 2020

Это кажется проблемой только из-за нескольких исключительных особенностей, связанных с типом модуля. ():

  1. () используется по умолчанию, когда тип возвращаемого значения опущен в сигнатуре функции ( поэтому fn main() эквивалентно fn main() -> ());
  2. И даже если вы не предоставили какое-либо выражение для возврата, пустые блоки или операторы в код также оценивается как ().

Пример ниже работает, потому что точка с запятой превращает выражение 5 в оператор, значение которого, следовательно, отбрасывается.

fn foo() {
    5;
}

Рекурсивно для всех спичечных рук легко вычислить (), когда ни одно из них не дает результат другого типа. Это имеет место при использовании return, потому что оператор return создает истинное расхождение с потоком выполнения: он оценивает тип never !, который приводит к любому другому типу .

fn foo(bar: i32) {
    match bar {
        1 => {
            return do_good_things(); // coerces to () because of the default match arm
        }
        0 => {
            return do_other_things(); // coerces to () because of the default match arm
        }
        _ => {
            // arm evaluates to (), oops
        }
    }
}

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

Следовательно, возможны следующие решения:

  1. Используйте другое тип возвращаемого значения для функции. Если нет ничего подходящего для возврата (например, только побочные эффекты), вы можете использовать почти любой тип, но другой тип единицы дает лучшую уверенность в том, что он станет абстракцией с нулевой стоимостью.

Детская площадка

struct Check;

fn foo(bar: i32) -> Check {
    match bar {
        1 => {
            do_good_things();
            Check
        }
        0 => {
            do_other_things();
            return Check; // can use return
        }
        _ => {
            // error[E0308]: expected struct Check, found ()
        }
    }
}
Не используйте операторы return или break и устанавливайте sh, что все ваши спички должны оцениваться не как ().

Игровая площадка

struct Check;

fn foo(bar: i32) {
    let _: Check = match bar {
        1 => {
            do_good_things();
            Check
        }
        0 => {
            do_other_things();
            Check
        }
        _ => {
            // error[E0308]: expected struct Check, found ()
        }
    };
}
Обратное: устанавливает sh, что выражение сопоставления оценивается как нулевой тип (такой как тип never !), так что никакая рука сопоставления не может вернуться из него, за исключением использования операторов потока управления, таких как break или return.

Детская площадка

enum Nope {}

fn foo(bar: i32) {
    let _: Nope = match bar {
        1 => {
            return do_good_things();
        }
        0 => {
            return do_other_things();
        }
        _ => {
            // error[E0308]: expected enum `Nope`, found ()
        }
    };
}

См. Также:

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...