Трейт Into
#![allow(unused)] fn main() { pub trait Into<T>: Sized { // Обязательный метод fn into(self) -> T; } }
Преобразование значение-в-значение, которое потребляет входное значение. Противоположность From.
Следует избегать реализации Into и вместо этого реализовывать From. Реализация From автоматически предоставляет реализацию Into благодаря обобщённой реализации в стандартной библиотеке.
Предпочитайте использование Into вместо From при указании трейт-границ для обобщённой функции, чтобы гарантировать, что типы, которые реализуют только Into, также могут быть использованы.
Примечание: Этот трейт не должен завершаться неудачей. Если преобразование может завершиться неудачей, используйте
TryInto.
Обобщённые реализации
From<T> for UподразумеваетInto<U> for TIntoрефлексивен, что означает, чтоInto<T> for Tреализован
Реализация Into для преобразований во внешние типы в старых версиях Rust
До Rust 1.41, если целевой тип не был частью текущего крейта, то нельзя было реализовать From напрямую. Например, рассмотрим этот код:
#![allow(unused)] fn main() { struct Wrapper<T>(Vec<T>); impl<T> From<Wrapper<T>> for Vec<T> { fn from(w: Wrapper<T>) -> Vec<T> { w.0 } } }
Этот код не компилировался в старых версиях языка, потому что правила сиротства Rust были немного более строгими. Чтобы обойти это, можно было реализовать Into напрямую:
#![allow(unused)] fn main() { struct Wrapper<T>(Vec<T>); impl<T> Into<Vec<T>> for Wrapper<T> { fn into(self) -> Vec<T> { self.0 } } }
Важно понимать, что Into не предоставляет реализацию From (в отличие от From, который предоставляет Into). Поэтому вы всегда должны пытаться реализовать From и затем возвращаться к Into, если From не может быть реализован.
Примеры
String реализует Into<Vec<u8>>:
Чтобы выразить, что мы хотим, чтобы обобщённая функция принимала все аргументы, которые могут быть преобразованы в указанный тип T, мы можем использовать трейт-границу Into<T>. Например: функция is_hello принимает все аргументы, которые могут быть преобразованы в Vec<u8>.
#![allow(unused)] fn main() { fn is_hello<T: Into<Vec<u8>>>(s: T) { let bytes = b"hello".to_vec(); assert_eq!(bytes, s.into()); } let s = "hello".to_string(); is_hello(s); }
Обязательные методы
1.0.0 · Source
#![allow(unused)] fn main() { fn into(self) -> T }
Преобразует этот тип в (обычно выводимый) входной тип.
Совместимость с dyn
Этот трейт не совместим с dyn.
В более старых версиях Rust совместимость с dyn называлась "объектной безопасностью", поэтому этот трейт не является объектно-безопасным.
Реализаторы
1.0.0 (const: unstable) · Source
#![allow(unused)] fn main() { impl<T, U> Into<U> for T where U: From<T>, }
Объяснение реализации
Приведённая выше обобщённая реализация означает:
Для любых типов T и U, если U реализует From<T>, то T автоматически реализует Into<U>
Это ключевая особенность взаимосвязи между From и Into - она обеспечивает автоматическую симметрию преобразований.
Примеры автоматических реализаций:
#![allow(unused)] fn main() { // Поскольку String реализует From<&str> // &str автоматически реализует Into<String> let s: String = "hello".into(); // Работает благодаря автоматической реализации // Поскольку Vec<u8> реализует From<String> // String автоматически реализует Into<Vec<u8>> let bytes: Vec<u8> = "hello".to_string().into(); // Преобразование String в Vec<u8> }
Практическое следствие:
Благодаря этой обобщённой реализации, достаточно реализовать только From, и соответствующая реализация Into будет предоставлена автоматически. Это предотвращает дублирование кода и обеспечивает согласованность преобразований.