Why does implementing From<T> on U enable calling T.into() to get U?
impl From<A> for B {
fn from(a: A) -> B {
// Convert A to B
}
}
By implementing the from() static method on B, you can convert an instance of A to B:
let a: A = ...;
let b: B = B::from(a); // This works
However, in practice, we often avoid using this directly, as it isn’t considered idiomatic Rust. Instead, we do the following:
let b: B = a.into();
Q: Wait a second, where does this .into() come from? We didn’t implement any into() function on A.
A: Correct, you didn’t implement it yourself. Rust provides it automatically. How?
This involves blanket implementations in Rust, which is a way of implementing something for all types. Essentially, this is done using a construct like:
impl<T> Trait1 for T {
...
}
In the case of the From trait, the Rust standard library provides the following blanket implementation:
impl<T, U> Into<U> for T // For all types T, implement Into<U>
where
U: From<T>, // If U already implements From<T>
{
fn into(self) -> U {
U::from(self)
}
}
This means that the standard library provides a blanket implementation of the Into<U> trait for all T where U already has an implementation of From<T>. In simpler terms, the compiler automatically implements the counterpart Into for you.
Effectively, it is as if the compiler wrote this for you:
impl Into<B> for A {
fn into(self) -> B {
B::from(self)
}
}
Thus, for any instance of A, you can call .into() to convert it to B.