Расходящиеся функции никогда не возвращают результат. Они помечены с помощью !, который является пустым типом.
#![allow(unused)]
fn main() {
fn foo() -> ! {
panic!("Этот вызов никогда не вернёт управление.");
}
}
В отличие от всех других типов, этот не может быть создан, потому что набор всех возможных значений этого типа пуст. Обратите внимание, что он отличается от типа (), который имеет ровно одно возможное значение.
Например, эта функция имеет возвращаемое значение, хотя о нём нет информации.
fn some_fn() {
()
}
fn main() {
let a: () = some_fn();
println!("Эта функция возвращает управление и вы можете увидеть эту строку.")
}
В отличие от этой функции, которая никогда не вернёт элемент управления вызывающей стороне.
#![feature(never_type)]
fn main() {
let x: ! = panic!("Этот вызов никогда не вернёт управление.");
println!("вы никогда не увидете эту строку!");
}
Хотя это может показаться абстрактным понятием, на самом деле это очень полезно и может пригодится. Главное преимущество этого типа в том, что его можно привести к любому другому типу и поэтому используется в местах, где требуется точный тип, например в ветвях match. Это позволяет нам писать такой код:
fn main() {
fn sum_odd_numbers(up_to: u32) -> u32 {
let mut acc = 0;
for i in 0..up_to {
// Обратите внимание, что возвращаемый тип этого выражения match должен быть u32
// потому что такой тип в переменной "addition" .
let addition: u32 = match i%2 == 1 {
// Переменная "i" типа u32, что совершенно нормально.
true => i,
// С другой стороны выражение "continue" не возвращает
// u32, но это тоже нормально, потому что это тип не возвращающий управление,
// не нарушает требования к типу выражения match.
false => continue,
};
acc += addition;
}
acc
}
println!("Сумма нечётных чисел до 9 (исключая): {}", sum_odd_numbers(9));
}
Это также возвращаемый тип функций, которые содержат вечный цикл (например, loop {}), как сетевые серверы или функции, завершающие процесс (например, exit()).