实现TryFrom/TryInto trait(而不是From/Into)

6
我尝试使用From特质来“重载”函数(使其能够接受结构体和字符串):
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'});
}

根据Playground的显示,此代码按预期工作。但是,由于字符串解析可能失败,我想使用try_into()而不是into()。因此如下:
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'});
}

问题:

  1. 不幸的是,上面的代码出现了错误 error[E0277]: '<T as TryInto<Measurement>>::Error' doesn't implement 'Debug'。为什么错误类型不是按照指定的String而是<T as TryInto<Measurement>>::Error?这个错误类型是什么意思?
  2. 我尝试使用let m = Measurement::try_from(value)代替let m = value.try_into()。但是出现了error[E0277]: the trait bound 'Measurement: From<T>' is not satisfied这似乎很奇怪,因为我调用的是try_from而不是from。为什么会这样呢?
  3. 如何正确实现TryFrom特性,以便可以按照概述处理解析错误?
  4. 为什么我们需要通过use std::convert::TryFromTryFrom引入作用域,而From特性不需要这样做呢?

@Stargateur 感谢您的快速修复:where <T as TryInto<Measurement>>::Error: std::fmt::Debug。但我也想了解为什么。为什么 try_into() 的错误类型不只是一个 String?因为我们有 type Error = String; - Phil-ZXX
1个回答

5
  1. associated type are still generic, why every implementation of TryInto<Measurement> would use String for TryFrom::Error ? Follow compiler hint. You could have an implementation for impl TryFrom<i32> for Measurement or 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),
        }
    }
    
  2. cause your bound is TryInto<Measurement> not TryFrom<&str>. TryInto have blanked implementation when T implement TryFrom, not the opposite.
  3. you did
  4. cause Rust 2018 didn't include it in prelude, Rust 2021 does

谢谢。只是出于好奇,关于第二点。将边界更改为T: TryFrom<&str>,然后使用let m = Measurement::try_from(value);仍会导致error[E0277]: the trait bound 'Measurement: From<T>' is not satisfied。为什么会这样? - Phil-ZXX
@Phil-ZXX https://play.integer32.com/?version=stable&mode=debug&edition=2021&gist=f35088683cfafa452630de84cd4ba83d 但不建议尝试,尝试使用限制会更好。 - Stargateur
谢谢!无论如何了解语法都是好的。 - Phil-ZXX

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接