How to understand trait FromWhatever in rust
The Rust book explains following:
The From trait allows for a type to define how to create itself from another type, hence providing a very simple mechanism for converting between several types.
Sounds simple enough. Lets try as simple example as possible:
use std::str::FromStr;
struct MyStructure {}
// auto accepted suggestion from language server.
impl FromStr for MyStructure {
type Err = (); // I've added this
fn from_str(_s: &str) -> Result<Self, Self::Err> {
Ok(Self {}) // I've added this
}
}
fn main() {
const INPUT: &str = "test";
let _tmp: MyStructure = MyStructure::from(INPUT);
}
Compiling test_range_2 v0.1.0 (/home/pavel/Repositories/test_range_2)
error[E0308]: mismatched types
--> src/main.rs:15:47
|
15 | let _tmp: MyStructure = MyStructure::from(INPUT);
| ----------------- ^^^^^ expected struct `MyStructure`, found `&str`
| |
| arguments to this function are incorrect
|
note: associated function defined here
--> /home/pavel/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/convert/mod.rs:374:8
|
374 | fn from(_: T) -> Self;
| ^^^^
For more information about this error, try `rustc --explain E0308`.
error: could not compile `test_range_2` due to previous error
So I expected this behavior:
let target: TARGET_TYPE = TARGET_TYPE::from::<SOURCE_TYPE>(input_var: SOURCE_TYPE);
Compared to the example in the rust book:
let num = Number::from(30);
It seems to me like a reasonable assumption.
However, reading the error message: "expected struct MyStructure, found &str". Does that mean that syntax is like this?
let target: TARGET_TYPE = TARGET_TYPE::from::<TARGET_TYPE>(input_var: TARGET_TYPE);
If, that’s true, then code in the rust book should also fail with the error "expected Number, found i32", but it doesn’t.
I expected my solution to work because I’ve implemented the trait FromStr, and I’m trying to create object from &str (see the "from" and "str"?). Which is also the type that was autocompleted by language server after I’ve typed impl FromStr for MyStructure. What am I missing? I want to impl FromStr for all my types but that compiler is not making it easy.
Solution
use std::str::FromStr;
struct MyStructure {}
impl FromStr for MyStructure {
type Err = ();
fn from_str(_s: &str) -> Result<Self, Self::Err> {
Ok(Self {})
}
}
impl From<&str> for MyStructure {
fn from(_: &str) -> Self {
Self {}
}
}
fn main() {
const INPUT: &str = "test";
let _tmp0 = MyStructure::from_str(INPUT);
let _tmp1 = MyStructure::from(INPUT);
}
I implemented trait FromStr but was referring to trait From<&str>. Since there is a trait in standard lib that looks like this:
trait From<T> -> T
compiler tried to use that in stead.
>Solution :
From and FromStr are completely different and entirely unrelated traits.
The first one performs 1 -> 1 infaillible conversions, while the latter is used to parse strings (it’s a pretty old trait, its modern equivalent is TryFrom but because FromStr hooks into str::parse() it remains convenient and popular).
Here you impl FromStr, which does nothing for your From::from call.
Instead you get told about a blanket implementation: converting a type to itself is a no-op, so the standard library has
impl<T> From<T> for T
which just returns itself.
Hence the compiler’s error message: when it encounters
MyStructure::from(INPUT);
and tries to resolve this to an implementation, the only impl From<?> for MyStructure it finds is the blanket implementation,
impl From<MyStructure> for MyStructure
and thus it tells you that it expects an argument of type MyStructure (the only possible input for the implementations it found), but found an &str (what you actually gave it).