Confusing messages related to turbofish operator

I have an enum which implements From taking various types, but I'm having trouble getting the correct implementation of the corresponding into method. An example:


enum Value {
    Integer(i64),
    Float(f64),
    String(String),
}

impl From<i64> for Value {
    fn from(value: i64) -> Self {
        Value::Integer(value)
    }
}

impl From<f64> for Value {
    fn from(value: f64) -> Self {
        Value::Float(value)
    }
}

impl From<String> for Value {
    fn from(value: String) -> Self {
        Value::String(value)
    }
}

impl From<&str> for Value {
    fn from(value: &str) -> Self {
        Value::String(value.to_string())
    }
}

fn main() {
    let v = Value::from("foo");

//    assert_eq!("foo".to_string(), v.into());
/*
 *  The above fails:
 *
error[E0282]: type annotations needed
  --> src/main.rs:35:37
   |
35 |     assert_eq!("foo".to_string(), v.into());
   |                                   --^^^^--
   |                                   | |
   |                                   | cannot infer type for type parameter `T`
   |                                   this method call resolves to `T`
 */
//    assert_eq!("foo".to_string(), v.into::<String>());
/*
 *  The above fails:
 *
error[E0107]: wrong number of type arguments: expected 0, found 1
  --> src/main.rs:48:44
   |
48 |     assert_eq!("foo".to_string(), v.into::<String>());
   |                                            ^^^^^^ unexpected type argument
 */
    assert_eq!("foo".to_string(), v.into<String>());
/*
 *  The above fails:
 *
error: chained comparison operators require parentheses
  --> src/main.rs:58:41
   |
58 |     assert_eq!("foo".to_string(), v.into<String>());
   |                                         ^^^^^^^^
   |
help: use `::<...>` instead of `<...>` to specify type arguments
   |
58 |     assert_eq!("foo".to_string(), v.into::<String>());
   |                                         ^^
 */
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error: chained comparison operators require parentheses
  --> src/main.rs:58:41
   |
58 |     assert_eq!("foo".to_string(), v.into<String>());
   |                                         ^^^^^^^^
   |
help: use `::<...>` instead of `<...>` to specify type arguments
   |
58 |     assert_eq!("foo".to_string(), v.into::<String>());
   |                                         ^^

error: aborting due to previous error

error: could not compile `playground`.

To learn more, run the command again with --verbose.

Notice how for the last example (line 58) the error message suggests using the form v.into::<String>(), which is what I used on line 48, but that led to an E0107 error.

Into::into does not have a generic type parameter, so you can't turbofish a call to it - the parameter is on the trait itself.

Some ways around this:

  • Fully qualify the method call, and turbofish the trait: Into::<String>::into(v)
  • Add an intermediate binding for the string with a type annotation: let foo: String = v.into()

But all of this is irrelevant, because you've not actually implemented the thing you're trying to use! You're trying to convert a Value to a String, but you only have code for turning a String into a Value, so there's no way for this code to work.

You need to either:

  • Implement From<Value> for String.
  • Do the conversion the other way around instead, e.g: assert_eq(Value::String(String::from("foo")), v).
    • In which case, you'll need to #[derive(PartialEq)] for Value.
2 Likes

But I was relying on this information, from here:

The Into trait is simply the reciprocal of the From trait. That is, if you have implemented the From trait for your type you get the Into implementation for free.

I suppose I misunderstood this - I took the above to mean that the compiler would implement the into() I wanted - what is it actually saying you get for free?

1 Like

I had the same misconception at first!*

If you implement From, you get Into for free - but both of these implementations are describing the same direction of conversion, just giving you slightly different ways to do it:

  • From<String> for Value allows you to call Value::from(my_string), which returns a Value.
  • Into<Value> for String allows you to call my_string.into(), which also returns a Value.

One is implemented on the target type, and one is implemented on the source type - this is what it means by the trait being the reciprocal. It does not mean that you get the reversed conversion for free - you still have to implement that yourself!

* This, incidentally, is why I dislike it when documentation uses words like 'simply' - it's really frustrating to be told something is simple and then find out you misunderstood it!

3 Likes

Hah! I knew it was too good to be true! Thanks for explaining - glad I'm not the only one confused by that bit of text!

Of course in the real situation I suppose I'd have to implement TryInto - after all, the value I'm trying to extract a string out of might not have a string inside.

1 Like