Polymorphism in Rust Traits

The From and Into traits are good examples of polymorphism.

If a trait uses a generic type T in its signature, such as in From:

pub trait From<T>: Sized {
    fn from(_: T) -> Self;
}

then different ‘variants’ of the trait, where <T> is replaced with concrete types, can be implemented. Here is an example:

struct Foo(i32);
#[derive(Debug)]
struct Bar(i32);

impl From<i32> for Bar {
    fn from(n: i32) -> Self {
        Bar(n)
    }
}

impl<'a> From<&'a Foo> for Bar {
    fn from(foo: &Foo) -> Self {
        Bar(foo.0)
    }
}

fn new_bar<T>(x: T) -> Bar
where
    T: Into<Bar>,
{
    x.into()
}

fn main() {
    let bar1 = Bar::from(1);
    println!("{:?}", bar1);
    let bar2 = Bar::from(&Foo(2));
    println!("{:?}", bar2);
    let bar3 = new_bar(3);
    println!("{:?}", bar3);
    let bar4 = new_bar(&Foo(4));
    println!("{:?}", bar4)
}
## Bar(1)
## Bar(2)
## Bar(3)
## Bar(4)

Here the From trait is implemented as From<i32> and From<&'a Foo> for Bar. After their implementation, Bar::from() can use either type as its argument. Under the hood, the type of the generic argument is inferred and used to decide whether to call Bar::from<i32>() or Bar::from<&'a Foo>(). These two types (i32 and &'a Foo) also get Into<Bar> implementations for free, which makes new_bar() work.