I tried to "overload" a function using the From trait (so that it can accept a struct and a string):
pub struct Measurement {
pub value: i16,
pub unit: char,
}
impl From<&str> for Measurement {
fn from(s: &str) -> Measurement {
let value = s[0..s.len() - 1].parse::<i16>().unwrap();
let unit = s.chars().last().unwrap();
return Measurement { value, unit };
}
}
pub fn print_measurement<T: Into<Measurement>>(value: T) {
let m = value.into();
println!("Measurement is {}{}", m.value, m.unit);
}
fn main() {
print_measurement("40m");
print_measurement(Measurement{value: 23, unit: 'g'});
}
As per Playground, this works as expected. However, since parsing of the string can fail, I wanted to use try_into(), instead of into(). So:
use std::convert::TryFrom;
#[derive(Debug)]
pub struct Measurement {
pub value: i16,
pub unit: char,
}
impl TryFrom<&str> for Measurement {
type Error = String;
fn try_from(s: &str) -> Result<Measurement, String> {
let value = s[0..s.len() - 1].parse::<i16>();
let unit = s.chars().last();
match (value, unit) {
(Ok(v), Some(u)) => Ok(Measurement { value: v, unit: u }),
_ => Err("Invalid value or unit".to_string()),
}
}
}
pub fn try_print_measurement<T: TryInto<Measurement>>(value: T) {
let m = value.try_into();
match m {
Ok(m) => println!("Measurement is {}{}", m.value, m.unit),
Err(e) => println!("Error when parsing: {:?}", e),
}
}
fn main() {
try_print_measurement("4_0m"); // <-- this line should fail to parse
try_print_measurement(Measurement{value: 23, unit: 'g'});
}
Questions:
- Unfortunately, the above fails with
error[E0277]: '<T as TryInto<Measurement>>::Error' doesn't implement 'Debug'. Why isn’t the error type equal toStringas specified, but rather<T as TryInto<Measurement>>::Error? And what does this error type mean? - Instead of
let m = value.try_into()I triedlet m = Measurement::try_from(value). But this fails witherror[E0277]: the trait bound 'Measurement: From<T>' is not satisfied, which seems odd as I calltry_from(and notfrom). Why is that? - How would one correctly implement the
TryFromtrait, so that the parsing errors can be dealt with as outlined? - Why do we need to bring
TryFrominto scope viause std::convert::TryFrom, whereas theFromtrait does not require this?
>Solution :
- associated type are still generic, why every implementation of
TryInto<Measurement>would useStringforTryFrom::Error? Follow compiler hint. You could have an implementation forimpl TryFrom<i32> for Measurementor whatever that use different associated type.pub fn try_print_measurement<T>(value: T) where T: TryInto<Measurement>, <T as TryInto<Measurement>>::Error: std::fmt::Debug, { let m = value.try_into(); match m { Ok(m) => println!("Measurement is {}{}", m.value, m.unit), Err(e) => println!("Error when parsing: {:?}", e), } } - cause your bound is
TryInto<Measurement>notTryFrom<&str>.TryIntohave blanked implementation whenTimplementTryFrom, not the opposite. - you did
- cause Rust 2018 didn’t include it in prelude, Rust 2021 does