Трейт 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 - Предсказуемое поведение
Безопасность типов
- Проверки во время компиляции
- Явная обработка возможных ошибок
Гибкость использования
- Может использоваться в трейт-границах
- Поддерживает цепочки преобразований
- Интегрируется с оператором
?