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

Phi*_*ZXX 7 traits rust

我尝试使用该特征“重载”函数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'});
}
Run Code Online (Sandbox Code Playgroud)

根据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'});
}
Run Code Online (Sandbox Code Playgroud)

问题:

  1. 不幸的是,上面的操作失败了error[E0277]: '<T as TryInto<Measurement>>::Error' doesn't implement 'Debug'。为什么错误类型不等于String指定的,而是等于<T as TryInto<Measurement>>::Error?这个错误类型是什么意思?
  2. 而不是let m = value.try_into()我尝试过let m = Measurement::try_from(value)。但这失败了error[E0277]: the trait bound 'Measurement: From<T>' is not satisfied,这看起来很奇怪,因为我称之为try_from(而不是from)。这是为什么?
  3. 如何正确实现该TryFrom特征,以便可以按照概述处理解析错误?
  4. 为什么我们需要将TryFromvia 纳入范围use std::convert::TryFrom,而FromTrait 不需要这样做?

Sta*_*eur 5

  1. 关联类型仍然是通用的,为什么每个实现TryInto<Measurement>都会使用Stringfor TryFrom::Error?遵循编译器提示。您可以有一个impl TryFrom<i32> for Measurement使用不同关联类型的实现或任何内容。
    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),
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 因为你的界限TryInto<Measurement>不是TryFrom<&str>。实施TryInto时已空白实施,而不是相反。TTryFrom
  3. 你做到了
  4. 因为 Rust 2018 没有将其包含在序言中,Rust 2021 包含了