From/Into Confusion

I've got an enum with a From impl:

pub enum OrderType {
    Market = 0,
    Limit = 1,
}

impl From<&str> for OrderType {
    fn from(order_type: &str) -> Self {
        match order_type {
            "MARKET" => OrderType::Market,
            "LIMIT" => OrderType::Limit,
            _ => {
                error!("Unknown order type: {}", order_type);
                OrderType::Market
            }
        }
    }

Like all Rust developers, I've read one million times that if you impl From you don't need to impl Into. So I tried this:

            let str: &str = order.order_type.into();

which fails to compile with

    = help: the trait `From<domain::OrderType>` is not implemented for `&str`
            but trait `From<mime::Name<'_>>` is implemented for it
    = help: for that trait implementation, expected `mime::Name<'_>`, found `domain::OrderType`
    = note: required for `domain::OrderType` to implement `Into<&str>`

unless I also impl Into:

impl Into<&str> for OrderType {
    fn into(self) -> &'static str {
        match self {
            OrderType::Market => "MARKET",
            OrderType::Limit => "LIMIT",
        }
    }
}

Thus, I am confused.

1 Like

Here you would like to convert an OrderType into an &str.

But you implemented:

This is a conversion from &str into OrderType. It's the other way around.

2 Likes

Implementing From vs Into is about conversions in the same direction, just with the syntax the other way around: since you have From<&str> for OrderType you do not need to implement Into<OrderType> for &str as the blanket implementation does it for you.

You need to implement impl From<OrderType> for &str to convert the other way.

3 Likes

So

impl From<&str> for OrderType

gets me

impl Into<OrderType> for &str

not

impl Into<&str> for OrderType
1 Like

Yes!

1 Like

I was 99% certain of that before I started the thread, but thought I'd post anyway as it'd result in good comments. And it did!

Controversial opinion: You should implement FromStr for OrderType instead of From<&str>.

  1. This operation is clearly fallible, so another option would be TryFrom<&str>
  2. FromStr allows for the more idiomatic opposite direction of str::parse() to create an OrderType from a &str.
6 Likes

There are also crates than can help avoid writing boilerplate and manually keeping things in sync (e.g. if you add a variant).

4 Likes