Перечисление Infallible

#![allow(unused)]
fn main() {
pub enum Infallible {}
}

Тип ошибки для ошибок, которые никогда не могут произойти.

Поскольку это перечисление не имеет вариантов, значение этого типа никогда не может фактически существовать. Это может быть полезно для обобщённых API, которые используют Result и параметризуют тип ошибки, чтобы указать, что результат всегда Ok.

Например, трейт TryFrom (преобразование, которое возвращает Result) имеет обобщённую реализацию для всех типов, где существует обратная реализация Into.

#![allow(unused)]
fn main() {
impl<T, U> TryFrom<U> for T
where
    U: Into<T>,
{
    type Error = Infallible;

    fn try_from(value: U) -> Result<Self, Infallible> {
        Ok(U::into(value))  // Никогда не возвращает `Err`
    }
}
}

Совместимость с будущими версиями

Это перечисление имеет ту же роль, что и тип ! ("never"), который нестабилен в этой версии Rust. Когда ! стабилизируется, мы планируем сделать Infallible псевдонимом типа для него:

#![allow(unused)]
fn main() {
pub type Infallible = !;
}

… и в конечном итоге объявить Infallible устаревшим.

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

#![allow(unused)]
fn main() {
trait MyTrait {}
impl MyTrait for fn() -> ! {}
impl MyTrait for fn() -> std::convert::Infallible {}
}

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

Реализации трейтов

Сводная таблица реализаций трейтов для Infallible

ТрейтМетод(ы)Описание реализации
Clonefn clone(&self) -> SelfНе может быть вызван (нет значений)
Copy(маркерный трейт)Разрешает копирование семантики
Debugfn fmt(&self, f: &mut Formatter<'_>) -> ResultФорматирование для отладки
Displayfn fmt(&self, f: &mut Formatter<'_>) -> ResultПользовательское форматирование
Errorsource(), description(), cause()Реализация для типа ошибки
Eqfn eq(&self, _: &Self) -> boolСравнение на равенство
Hashfn hash<H: Hasher>(&self, _: &mut H)Хеширование значения
Ordcmp(), max(), min(), clamp()Сравнение для упорядочивания
PartialEqfn eq(&self, _: &Self) -> boolЧастичное равенство
PartialOrdpartial_cmp(), lt(), le(), gt(), ge()Частичное упорядочивание
StructuralEq(маркерный трейт)Для сопоставления с образцом
StructuralPartialEq(маркерный трейт)Для частичного сопоставления

Особенности реализаций

Все реализации безусловны

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

Маркерные трейты

  • Copy, StructuralEq, StructuralPartialEq - маркерные трейты без методов
  • Гарантируют определённое поведение на уровне типажа

Трейты ошибок

Error реализован, что позволяет использовать Infallible как тип ошибки в Result, где ошибка никогда не происходит.

Сравнение и хеширование

Все методы сравнения и хеширования тривиальны, так как никогда не вызываются.

Практическое использование

В обобщённых API

#![allow(unused)]
fn main() {
fn always_succeeds() -> Result<i32, Infallible> {
    Ok(42)  // Ошибка никогда не может произойти
}
}

С TryFrom/TryInto

#![allow(unused)]
fn main() {
// Преобразование, которое никогда не fails
let x: i32 = i32::try_from(10u32).unwrap(); // Можно безопасно использовать unwrap
}

В пользовательских типах

#![allow(unused)]
fn main() {
enum MyResult<T> {
    Ok(T),
    Err(Infallible), // Указывает, что ошибка невозможна
}
}

Будущее развитие

Infallible является временным решением до стабилизации типа ! (never type). После стабилизации все текущие использования Infallible будут автоматически перенаправлены на !.