Трейт 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 T
  • Into рефлексивен, что означает, что 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 будет предоставлена автоматически. Это предотвращает дублирование кода и обеспечивает согласованность преобразований.