1

I am using the From trait to convert an i32 to a structure of my own. I use this conversion in a generic function do_stuff that doesn't compile:

use std::convert::*;

struct StoredValue {
    val: i32,
}

impl From<i32> for StoredValue {
    fn from(value: i32) -> StoredValue {
        return StoredValue {val: value};
    }
}

/// Generic function that stores a value and do stuff
fn do_stuff<T>(value: T) -> i32 where T: From<T> {
    let result = StoredValue::from(value);
    // .... do stuff and
    return 0;
}

fn main () {
    let result = do_stuff(0); // call with explicit type
}

and the compilation error:

main.rs:15:18: 15:35 error: the trait `core::convert::From<T>` is not implemented for the type `StoredValue` [E0277]
main.rs:15     let result = StoredValue::from(value);

Does it make sense to implement a generic version of From<T> for StoredValue?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
cpichard
  • 11
  • 2

2 Answers2

2

Your generic function is saying, "I accept any type that implements being created from itself." Which isn't what you want.

There's a few things you could be wanting to say:

"I accept any type that can be converted into an i32 so that I can create a StoredValue." This works because you know StoredValue implements From<i32>.

fn do_stuff<T>(value: T) -> i32 where T: Into<i32> {
    let result = StoredValue::from(value.into());
    // ...
}

Or, "I accept any type that can be converted into a StoredValue." There is a handy trait that goes along with the From<T> trait, and it's called Into<T>.

fn do_stuff<T>(value: T) -> i32 where T: Into<StoredValue> {
    let result = value.into();
    // ...
}

The way to remember how/when to use these two traits that go hand in hand is this:

  • Use Into<T> when you know what you want the end result to be, i.e from ?->T
  • Use From<T> when you know what you have to start with, but not the end result, i.e. T->?

The reason these two traits can go hand in hand together, is if you have a T that implements Into<U>, and you have V that implements From<U> you can get from a T->U->V.

The Rust std lib has such a conversion already baked in that says, "Any type T that implements From<U>, than U implements Into<T>."

Because of this, when you implemented From<i32> for StoredValue you can assume there is a Into<StoredValue> for i32.

kbknapp
  • 172
  • 1
  • 5
  • Thanks for the explanation, I chose to use From because I saw in the documentation that it was preferable to use From instead of Into. Is there any reason for that ? – cpichard May 21 '16 at 16:21
  • @cpichard It's preferable to *implement* `From`, but *consuming* `Into` is often better than consuming `From` since it reads more naturally and allows more conversions than `From`. –  May 21 '16 at 17:02
1

To make do_stuff() work, it must be possible to convert type T into StoredValue. So its declaration should be

fn do_stuff<T>(value: T) -> i32 where StoredValue: From<T> {

Edit: I agree with Shepmaster that that should better be

fn do_stuff<T>(value: T) -> i32 where T: Into<StoredValue> {
    let result = value.into();
    // ...

Since there is a generic implementation that turns T: From<U> into U: Into<T>, this allows to use both kinds of conversions, those implementing From and those implementing Into. With my first version only conversions implementing From would work.

starblue
  • 55,348
  • 14
  • 97
  • 151
  • Or (mostly) conversely `T: Into`. [When should I implement std::convert::From vs std::convert::Into?](http://stackoverflow.com/q/29812530/155423) may have more detail on the difference. – Shepmaster May 21 '16 at 15:45
  • Thanks ! I understand my mistake now, I didn't think it was possible to add types different than he generic ones in a where clause. That totally makes sense now. – cpichard May 21 '16 at 16:24
  • indeed, where T: Into feels more readable. Thanks ! – cpichard May 22 '16 at 07:40
  • unfortunately `where T: Into` doesn't compile with this example, it looks like `Into` has to be implemented, too bad. – cpichard May 22 '16 at 07:55
  • Works for me, but you also have to change the following line. (I forgot that, added it now.) – starblue May 22 '16 at 10:40