Трейт TryInto

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

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

Попытка преобразования, которая потребляет self и может быть как дорогой, так и дешёвой.

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

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

Реализация TryInto

Это страдает от тех же ограничений и обоснований, что и реализация Into, см. там для подробностей.

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

1.34.0 · Source

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

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

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

1.34.0 · Source

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

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

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

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

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

Реализаторы

1.34.0 (const: unstable) · Source

#![allow(unused)]
fn main() {
impl<T, U> TryInto<U> for T
where
    U: TryFrom<T>,
}

Объяснение реализации

Приведённая выше обобщённая реализация означает:

Для любых типов T и U, если U реализует TryFrom<T>, то T автоматически реализует TryInto<U>

Эта реализация обеспечивает автоматическую симметрию между TryFrom и TryInto, аналогично связи между From и Into.

Практическое следствие:

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

Примеры использования

Базовое использование

#![allow(unused)]
fn main() {
use std::convert::TryInto;

let x: i32 = 5;
let y: u8 = x.try_into().unwrap(); // Успешно для 5
println!("{}", y); // 5

let x: i32 = 256;
let y: Result<u8, _> = x.try_into(); // Ошибка - значение слишком большое для u8
assert!(y.is_err());
}

Использование в обобщённых функциях

#![allow(unused)]
fn main() {
use std::convert::TryInto;

fn process_number<T, U>(value: T) -> Result<U, U::Error>
where
    T: TryInto<U>,
{
    value.try_into()
}

let result: Result<u8, _> = process_number(100i32);
assert_eq!(result, Ok(100));

let result: Result<u8, _> = process_number(1000i32);
assert!(result.is_err());
}

Цепочка преобразований с обработкой ошибок

#![allow(unused)]
fn main() {
use std::convert::TryInto;

fn convert_safely(value: i64) -> Result<i32, Box<dyn std::error::Error>> {
    let medium: i32 = value.try_into()?;
    let small: i16 = medium.try_into()?;
    Ok(small)
}

println!("{:?}", convert_safely(1000)); // Ok(1000)
println!("{:?}", convert_safely(1_000_000_000)); // Err (переполнение i16)
}

Сравнение с TryFrom

TryFrom (предпочтительный для реализации)

#![allow(unused)]
fn main() {
impl TryFrom<i32> for u8 {
    type Error = TryFromIntError;
    
    fn try_from(value: i32) -> Result<Self, Self::Error> {
        // логика преобразования
    }
}
}

TryInto (автоматически предоставляется)

#![allow(unused)]
fn main() {
// Автоматически генерируется из TryFrom
let x: i32 = 100;
let y: u8 = u8::try_from(x).unwrap(); // Явный вызов TryFrom
let z: u8 = x.try_into().unwrap();    // Автоматический вызов TryInto
}

Обработка различных типов ошибок

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

#![allow(unused)]
fn main() {
use std::convert::{TryInto, TryFrom};

// Числовые преобразования
match 1000i32.try_into() as Result<u8, _> {
    Ok(val) => println!("Успех: {}", val),
    Err(_) => println!("Число слишком большое"),
}

// Строковые преобразования  
match vec![b'h', b'i', 0].try_into() as Result<std::ffi::CString, _> {
    Ok(cstr) => println!("Успешное CString"),
    Err(e) => println!("Ошибка создания CString: {:?}", e),
}
}

Ключевые преимущества

Согласованность с существующим кодом

  • Единообразный API с Into/From
  • Предсказуемое поведение

Безопасность типов

  • Проверки во время компиляции
  • Явная обработка возможных ошибок

Гибкость использования

  • Может использоваться в трейт-границах
  • Поддерживает цепочки преобразований
  • Интегрируется с оператором ?