Blanket implementations
Now for a very fun part, you can implement traits on generic types!
Implementing a trait on a generic type will make its elements available to every type that fits the trait bounds. You may be asking yourself: Why in the world would I want that, that sounds like a recipe for disaster! But actually, it's not so bad.
Firstly, you have to import the trait into the module you want to use a blanket implementation in, so "accidental" use can't happen. Secondly, there's the orphan rule:
If you implement a trait on a type, either the trait or the type (or both) has to originate from the crate the impl
statement is in. That means you can't attach foreign traits to foreign types, so accidentally changing functionality in foreign code also is something that can't happen.
With that in mind, we will look at two examples from the standard library: ToString
and Into
.
ToString
expresses the contents of an object as a new String
. This is automatically implemented for any object that implements Display
. You might know where this is going: The implementation creates a new String
object and just prints into it.
This works because print!
works like just a specific form of write!
that always prints to stdout.
write!
can print to any object that implements the Write
trait, which String
does, coincidentally. It uses a shortcut internally, but you could implement a working version yourself with this knowledge.
Into
is one of two type conversion traits in the standard library. The interesting bit is that it gets automatically implemented for any type whose target type implements From
with it.
Just have a look at the original library source code:
impl<T, U> Into<U> for T
where
U: From<T>,
{
fn into(self) -> U {
U::from(self)
}
}