Reputation: 11
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
?
Upvotes: 1
Views: 184
Reputation: 56772
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.
Upvotes: 1
Reputation: 302
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:
Into<T>
when you know what you want the end result to be, i.e from ?->T
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
.
Upvotes: 2