如何在Rust中使用泛型?

比如从一个字符串读入转换为矩阵

struct Matrix<T> {
    containner: Box<Vec<Vec<T>>>,
    size: (usize, usize),
}

impl std::str::FromStr for Matrix<i8> {
    type Err = std::num::ParseIntError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let parse_out: Vec<Vec<i8>> = s
            .split(';')
            .map(|i| i.split(',').map(|j| i8::from_str(j).unwrap()).collect())
            .collect();

        let n = parse_out.len();
        let m = parse_out.iter().map(|x| x.len()).max().unwrap();

        let mat = Matrix::<i8> {
            containner: Box::new(parse_out),
            size: (m, n),
        };

        return Ok(mat);
    }
}

在编写这个矩阵时遇到三个问题:

  1. 怎么限制类型Ti8, i16...u8, u16...f32, f64, isize, usize等等一系列数字类型?
  2. 为Matirx实现from_str方法时,如果不具体指定类型,首先type Err无法指定,其次在T::from_str(j).unwrap()时会报错,提示unwrap不一定有实现(这似乎是一个问题?)。如果指定类型,则会有大量代码重复,因为要为每一个类型都重写一遍。
  3. 本身from_str指定了参数和返回类型,但是现在方法中需要额外的参数(行和列的分隔符),应该怎么设计?这里我因作业需要,直接指定为,;了。

想了想了一会,感觉

  1. 类型那个可能需要定义宏???我刚刚看完rust的官方教程,感觉教程上关于宏的内容讲的好少,就是简单提一下。看完好像意思是生成代码的代码。emmm
  2. 如果实在不行,可能还需要自己撸一个num类,以及一系列我觉得该有但是没有的。
  3. crates.io上翻到了num-trait包。看来rust的官方库确实有很多该有的没有。┑( ̄Д  ̄)┍
阅读 2k
1 个回答
  1. 可以通过 marker trait (没有方法、只做标识)实现:

    pub trait Num;
    
    impl Num for i8 {}
    impl Num for i16 {}
    impl Num for i32 {}
    
    pub struct Matrix<T> where T: Num { ... }

    如果不允许其他人扩展,可以参考:Sealed traits protect against downstream implementations (C-SEALED)套一个私有模块。

  2. 可以转成自己的错误类型,也可以这样用直接用已有的错误类型:

    impl<T> std::str::FromStr for Matrix<T> 
      where T: Num + FromStr, <T as FromStr>::Err: std::fmt::Debug {    // 这里
      type Err = <T as FromStr>::Err; // 这里
    
      fn from_str(s: &str) -> Result<Self, Self::Err> {
          let parse_out: Vec<Vec<T>> = s
              .split(';')
              .map(|i| i.split(',').map(|j| j.parse::<T>().unwrap()).collect())
              .collect();
    
          let n = parse_out.len();
          let m = parse_out.iter().map(|x| x.len()).max().unwrap();
    
          let mat = Matrix::<T> {
              containner: Box::new(parse_out),
              size: (m, n),
          };
    
          return Ok(mat);
      }
    }

    建议是用自己的错误类型。

  3. 单独定义一个解析方法,不用硬套到 FromStr 上。例如数字能跟进制解析,所以整数类型都有个单独的 from_str_radix 方法。
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进