【问题标题】:Using a generic in a struct and implementing via a trait在结构中使用泛型并通过特征实现
【发布时间】:2016-05-03 08:57:37
【问题描述】:

我有这样定义的结构

struct Shape<T> {
    line_one: T,
    line_two: T,
}

我正在尝试创建一个简单的特征和实现,使用这个结构来计算一些简单的数学。

我的 trait 和 impls 看起来像这样

trait Calculate<T: Mul<Output = T>> {
    fn calc(&self, Shape: T) -> T;
}

impl<T> Calculate<T> for Shape<T> {
    fn calc(&self, Shape: T) -> T {
        self.line_one * 2 + self.line_two * 2
    }
}

impl Calculate<i32> {
    fn calc(&self) -> i32 {
        self.line_one * 2 + self.line_two * 2
    }
}

fn calc_fn<T: Calculate<i32>>(calculate: T) {
    calculate.calc();
}

当我将它放入 Rust 操场时,编译失败,因为 Mul 未在 impl Calculate&lt;T&gt; 中实现。但是,如果我将 &lt;T&gt; 更改为 &lt;T: Mul&lt;Output = T&gt;&gt;,我会收到以下错误:

<anon>:14:21: 14:22 error: expected one of `!`, `(`, `+`, `,`, `::`, `<`, or `>`, found `:`
<anon>:14 impl <T> Calculate<T: Mul<Output = T>> for Shape<T> 

我不知道如何在impl Calculate&lt;T&gt; 中为T 实现Mul。

我想要实现的是我可以发送 f32 或 i32 的代码,而无需创建两个不同的 impl 定义,因此只需传入 T。

【问题讨论】:

    标签: generics rust


    【解决方案1】:

    此语法不正确:

    impl<T> Calculate<T: Mul<Output = T>> for Shape<T>
    

    你想要的

    impl<T: Mul<Output = T>> Calculate<T> for Shape<T>
    

    我几乎总是建议改用where 子句;我认为它读起来更好,可能有助于防止这种情况:

    impl<T> Calculate<T> for Shape<T>
        where T: Mul<Output = T>,
    

    这只会解锁更多错误。您正试图乘以2,这是一个尚未完全确定的整数变量——它是u8吗?是i32 吗?这些限制并不是说T 可以乘以2。此外,您尝试将值相加,但不能保证您可以添加类型 T。

    impl Calculate&lt;i32&gt; 和 calc_fn 块实际上没有意义;您不太可能想要 implement functions for the trait 并且您没有在后者中提供 Shape。该 trait 也不使用 Shape: T 参数(而且 Rust 使用 snake_case 变量名)。

    我想要实现的是我可以发送 f32 或 i32 的代码,而无需创建两个不同的 impl 定义,因此只需传入 T。

    在这种情况下,可能会更容易:

    trait Calculate<T> {
        fn calc(&self) -> T;
    }
    
    impl Calculate<i32> for Shape<i32> {
        fn calc(&self) -> i32 {
            self.line_one * 2 + self.line_two * 2
        }
    }
    
    impl Calculate<f32> for Shape<f32> {
        fn calc(&self) -> f32 {
            self.line_one * 2.0 + self.line_two * 2.0
        }
    }
    

    请注意,这些是不等效的! f64 具有浮点文字 (2.0) 而非整数 (2)。

    如果您必须有一个泛型,则需要将文字 2 转换为类型 T 或添加一个限制,即 T 可以乘以某个已知大小。不幸的是,f64 不能在不强制转换的情况下乘以任何整数值。

    num crate 中的FromPrimitive trait 是这里的关键。使类型Copy 也使实现更容易。

    extern crate num;
    
    use std::ops::{Add, Mul};
    use num::FromPrimitive;
    
    struct Shape<T> {
        line_one: T,
        line_two: T,
    }
    
    trait Calculate<T> {
        fn calc(&self) -> T;
    }
    
    impl<T> Calculate<T> for Shape<T>
        where T: Copy + FromPrimitive + Add<Output = T> + Mul<Output = T>,
    {
        fn calc(&self) -> T {
            let two = T::from_u8(2).expect("Unable to create a value of two");
            self.line_one * two + self.line_two * two
        }
    }
    
    fn main() {
        let s1 = Shape { line_one: 2, line_two: 3 };
        let s2 = Shape { line_one: 2.0, line_two: 3.0 };
    
        println!("{}", s1.calc());
        println!("{}", s2.calc());
    }
    

    【讨论】:

    • 谢谢。我有一个侦察员(主要是出于兴趣),发现我只能使用 let x: T = num::NumCast::from(2i32); 将文字转换为 T 类型。但是大多数 num casts 已从 std 中删除 - 任何关于将文字转换为 T 的指针?
    • @Nodoid 使用 FromPrimitive 有什么问题吗,如上一个示例所示?
    猜你喜欢
    • 1970-01-01
    • 2017-10-18
    • 1970-01-01
    • 1970-01-01
    • 2019-04-04
    • 2017-01-04
    • 2023-03-17
    相关资源
    最近更新 更多