Трейт TryFrom

#![allow(unused)]
fn main() {
pub trait TryFrom<T>: Sized {
    type Error;

    // Обязательный метод
    fn try_from(value: T) -> Result<Self, Self::Error>;
}
}

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

Это полезно, когда вы выполняете преобразование типа, которое может тривиально завершиться успехом, но также может потребовать специальной обработки. Например, нет способа преобразовать i64 в i32 с помощью трейта From, потому что i64 может содержать значение, которое i32 не может представить, и поэтому преобразование приведёт к потере данных. Это можно обработать путём усечения i64 до i32, простого возврата i32::MAX или каким-либо другим методом. Трейт From предназначен для безупречных преобразований, поэтому трейт TryFrom информирует программиста, когда преобразование типа может пойти плохо, и позволяет ему решить, как с этим справиться.

Обобщённые реализации

  • TryFrom<T> for U подразумевает TryInto<U> for T
  • try_from рефлексивен, что означает, что TryFrom<T> for T реализован и не может завершиться неудачей - связанный тип Error для вызова T::try_from() на значении типа T является Infallible. Когда тип ! стабилизируется, Infallible и ! будут эквивалентны.

Предпочитайте использование TryInto вместо TryFrom при указании трейт-границ для обобщённой функции, чтобы гарантировать, что типы, которые реализуют только TryInto, также могут быть использованы.

TryFrom<T> может быть реализован следующим образом:

#![allow(unused)]
fn main() {
struct GreaterThanZero(i32);

impl TryFrom<i32> for GreaterThanZero {
    type Error = &'static str;

    fn try_from(value: i32) -> Result<Self, Self::Error> {
        if value <= 0 {
            Err("GreaterThanZero only accepts values greater than zero!")
        } else {
            Ok(GreaterThanZero(value))
        }
    }
}
}

Примеры

Как описано, i32 реализует TryFrom<i64>:

#![allow(unused)]
fn main() {
let big_number = 1_000_000_000_000i64;
// Тихо усекает `big_number`, требует обнаружения
// и обработки усечения после факта.
let smaller_number = big_number as i32;
assert_eq!(smaller_number, -727379968);

// Возвращает ошибку, потому что `big_number` слишком велик,
// чтобы поместиться в `i32`.
let try_smaller_number = i32::try_from(big_number);
assert!(try_smaller_number.is_err());

// Возвращает `Ok(3)`.
let try_successful_smaller_number = i32::try_from(3);
assert!(try_successful_smaller_number.is_ok());
}

Обязательные связанные типы

1.34.0 · Source

#![allow(unused)]
fn main() {
type Error
}

Тип, возвращаемый в случае ошибки преобразования.

Обязательные методы

1.34.0 · Source

#![allow(unused)]
fn main() {
fn try_from(value: T) -> Result<Self, Self::Error>
}

Выполняет преобразование.

Совместимость с dyn

Этот трейт не совместим с dyn.

В более старых версиях Rust совместимость с dyn называлась "объектной безопасностью", поэтому этот трейт не является объектно-безопасным.

Реализаторы

Сводная таблица реализаций трейта TryFrom<T>

Целевой типИсходный типТип ошибкиУсловия успешного преобразования
i8i16, i32, i64, i128, isizeTryFromIntErrorЗначение в пределах i8::MIN..=i8::MAX
i16i32, i64, i128, isizeTryFromIntErrorЗначение в пределах i16::MIN..=i16::MAX
i32i64, i128, isizeTryFromIntErrorЗначение в пределах i32::MIN..=i32::MAX
i64i128TryFromIntErrorЗначение в пределах i64::MIN..=i64::MAX
u8i8, i16, i32, i64, i128, isize, u16, u32, u64, u128, usizeTryFromIntErrorДля знаковых: значение ≥ 0 и ≤ u8::MAX
Для беззнаковых: значение ≤ u8::MAX
u16i16, i32, i64, i128, isize, u32, u64, u128, usizeTryFromIntErrorДля знаковых: значение ≥ 0 и ≤ u16::MAX
Для беззнаковых: значение ≤ u16::MAX
u32i32, i64, i128, isize, u64, u128, usizeTryFromIntErrorДля знаковых: значение ≥ 0 и ≤ u32::MAX
Для беззнаковых: значение ≤ u32::MAX
u64i64, i128, u128TryFromIntErrorДля знаковых: значение ≥ 0 и ≤ u64::MAX
Для беззнаковых: значение ≤ u64::MAX
usizei8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128TryFromIntErrorЗависит от архитектуры платформы
isizei8, i16, i32, i64, i128, u8, u16, u32, u64, u128, usizeTryFromIntErrorЗависит от архитектуры платформы
charu8CharTryFromErrorЗначение в пределах 0..=0x7F (валидный ASCII)
CStringVec<u8>FromVecWithNulErrorВектор не содержит нулевых байтов
OsStringPathBuf(нет ошибки)Всегда успешно
PathBufOsString(нет ошибки)Всегда успешно
String&str(нет ошибки)Всегда успешно
Vec<T>VecDeque<T>, BinaryHeap<T>, LinkedList<T>, Box<[T]>(нет ошибки)Всегда успешно

Дополнительные реализации для примитивных типов

Преобразования между целыми числами

  • Все комбинации между знаковыми и беззнаковыми целыми
  • Проверка на переполнение и отрицательные значения
  • Возвращает TryFromIntError при нарушении границ

Строковые преобразования

#![allow(unused)]
fn main() {
// char из u8 (только ASCII)
let ascii_char = char::try_from(65u8).unwrap(); // 'A'
let invalid_char = char::try_from(255u8); // Err(CharTryFromError)

// CString из Vec<u8>
let valid_vec = vec![b'h', b'i'];
let cstring = CString::try_from(valid_vec).unwrap();
let invalid_vec = vec![0, 1, 2];
let error = CString::try_from(invalid_vec); // Err(FromVecWithNulError)
}

Особенности обработки ошибок

TryFromIntError

  • Для всех числовых преобразований между целыми типами
  • Не содержит дополнительной информации о причине ошибки
  • Реализует Debug, Display, Error

CharTryFromError

  • Специфична для преобразований в char
  • Возникает при невалидных значениях Unicode

FromVecWithNulError

  • Для преобразований в CString
  • Возникает при наличии нулевых байтов в векторе
  • Предоставляет метод into_bytes() для восстановления исходных данных