Using Into to Elegantly Coerce str and String types in Rust

Rust Polymorphism vs C++ Polymorphism

C++ has splitting method overloading, Rust has joining method overloading. In other mainstream languages it is common to overload a method name from one type to others. This, I call, "splitting polymorphism". Rust does not permit splitting polymorphism for one reason or another. Instead Rust encourages methods to be overloaded from other types to one. This, I call, "joining polymorphism". A simple example that is very handy according to the Rust library is using the Into trait to join the str and String types. It can be fairly annoying to deal with these two string types on top of the indirection of what is or is not and how many references that have been attached to the ground type.

Joining str and String with Into/From

Conveniently, the From<&str> type, which implements Into, is provided by the std library. This means that we can write code like:

let my_string: String = my_str.into();

Generalizing usage of Joining Polymorphism

In general to use joining polymorphism you will implement a trait that induces a type collision on the right hand side of its type signature. Often this right hand side collision will be slightly obscured by a definition that uses the Self type. Do not let this confuse you, not all trait definitions are made equal. For reference, here is an Into implementation from the Rust documentation:

struct Wrapper<T>(Vec<T>);
impl<T> Into<Vec<T>> for Wrapper<T> {
    fn into(self) -> Vec<T> {
        self.0
    }
}

In the above code notice that any trait that also returns a Vec will have a similar .into() semantic. This is how polymorphism in Rust is implemented, which can be a bit confusing if you come from a splitting polymorphism language. Now to put Into to use for us, we can write a parametric function like so:

pub fn foo<S: Into<String>>(s: S) {
   ...
}

Implicit Deref Coercion for String to str

It is also possible to go the other way from a String to a str by using a separate trait than From and Into. The Deref trait provides the opportunity for a similar type coercion when referencing one type to coerce the type into another type. This is most commonly seen in practical use to go from a String reference to a str reference:

pub fn foo(s: &str) {
   ...
}
...
foo(&my_string)

In the above code, the String my_string is implicitly converted to a str. This implementation also does not require a copy or clone, so it is thought to be less expensive than the Into example.