Files
100-exercises-to-learn-rust/book/src/04_traits/09_from.md
Luca Palmieri 96f06708b0 Render the book in PDF using pandoc and LaTeX. (#126)
* Render the book in PDF using `pandoc` and LaTeX.

* Fix installs.

* Go the apt-get route

* Another attempt

* Avoid installing twice.

* Re-order.

* Add more packages.

* Minimise deps. Fix link checker.

* Missing package.

* Missing package.

* Missing package.

* More packages.

* Missing package.

* Missing package.

* More packages...

* Remove.

* Fix link checker.

* Fix link checker.

* Fix path.

* Add subtitle.

* Avoid running over the right margin.

* Avoid running over the right margin.

* Formatting
2024-08-05 17:52:15 +02:00

3.7 KiB
Raw Blame History

From and Into

Lets go back to where our string journey started:

let ticket = Ticket::new(
    "A title".into(), 
    "A description".into(), 
    "To-Do".into()
);

We now know enough to start unpacking what .into() is doing here.

The problem

This is the signature of the new method:

impl Ticket {
    pub fn new(
        title: String, 
        description: String, 
        status: String
    ) -> Self {
        // [...]
    }
}

Weve also seen that string literals (such as "A title") are of type &str.
We have a type mismatch here: a String is expected, but we have a &str. No magical coercion will come to save us this time; we need to perform a conversion.

From and Into

The Rust standard library defines two traits for infallible conversions: From and Into, in the std::convert module.

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

pub trait Into<T>: Sized {
    fn into(self) -> T;
}

These trait definitions showcase a few concepts that we havent seen before: supertraits and implicit trait bounds. Lets unpack those first.

Supertrait / Subtrait

The From: Sized syntax implies that From is a subtrait of Sized: any type that implements From must also implement Sized. Alternatively, you could say that Sized is a supertrait of From.

Implicit trait bounds

Every time you have a generic type parameter, the compiler implicitly assumes that its Sized.

For example:

pub struct Foo<T> {
    inner: T,
}

is actually equivalent to:

pub struct Foo<T: Sized> 
{
    inner: T,
}

In the case of From<T>, the trait definition is equivalent to:

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

In other words, both T and the type implementing From<T> must be Sized, even though the former bound is implicit.

Negative trait bounds

You can opt out of the implicit Sized bound with a negative trait bound:

pub struct Foo<T: ?Sized> {
    //            ^^^^^^^
    //            This is a negative trait bound
    inner: T,
}

This syntax reads as “T may or may not be Sized”, and it allows you to bind T to a DST (e.g. Foo<str>). It is a special case, though: negative trait bounds are exclusive to Sized, you cant use them with other traits.

&str to String

In stds documentation you can see which std types implement the From trait.
Youll find that String implements From<&str> for String. Thus, we can write:

let title = String::from("A title");

Weve been primarily using .into(), though.
If you check out the implementors of Into you wont find Into<String> for &str. Whats going on?

From and Into are dual traits.
In particular, Into is implemented for any type that implements From using a blanket implementation:

impl<T, U> Into<U> for T
where
    U: From<T>,
{
    fn into(self) -> U {
        U::from(self)
    }
}

If a type U implements From<T>, then Into<U> for T is automatically implemented. Thats why we can write let title = "A title".into();.

.into()

Every time you see .into(), youre witnessing a conversion between types.
Whats the target type, though?

In most cases, the target type is either:

  • Specified by the signature of a function/method (e.g. Ticket::new in our example above)
  • Specified in the variable declaration with a type annotation (e.g. let title: String = "A title".into();)

.into() will work out of the box as long as the compiler can infer the target type from the context without ambiguity.