【问题标题】:Overloading an operator for all structs with a trait in Rust在 Rust 中为所有具有特征的结构重载运算符
【发布时间】:2021-02-12 22:33:53
【问题描述】:

我正在尝试使用特征和运算符重载在 Rust 中实现 C++ 样式的表达式模板。我在尝试为每个表达式模板结构重载“+”和“*”时陷入困境。编译器抱怨 AddMul trait 实现:

error[E0210]: type parameter `T` must be used as the type parameter for some local type (e.g., `MyStruct<T>`)
  --> src/main.rs:32:6
   |
32 | impl<T: HasValue + Copy, O: HasValue + Copy> Add<O> for T {
   |      ^ type parameter `T` must be used as the type parameter for some local type
   |
   = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local
   = note: only traits defined in the current crate can be implemented for a type parameter

如果我尝试为其实现特征的类型在没有我的 crate 的情况下可构造,那么该错误将是有意义的,但该类型是必须实现我定义的 HasValue 特征的泛型。

代码如下:

use std::ops::{Add, Mul};

trait HasValue {
    fn get_value(&self) -> i32;
}

// Val

struct Val {
    value: i32,
}

impl HasValue for Val {
    fn get_value(&self) -> i32 {
        self.value
    }
}

// Add

struct AddOp<T1: HasValue + Copy, T2: HasValue + Copy> {
    lhs: T1,
    rhs: T2,
}

impl<T1: HasValue + Copy, T2: HasValue + Copy> HasValue for AddOp<T1, T2> {
    fn get_value(&self) -> i32 {
        self.lhs.get_value() + self.rhs.get_value()
    }
}

impl<T: HasValue + Copy, O: HasValue + Copy> Add<O> for T {
    type Output = AddOp<T, O>;
    fn add(&self, other: &O) -> AddOp<T, O> {
        AddOp {
            lhs: *self,
            rhs: *other,
        }
    }
}

// Mul

struct MulOp<T1: HasValue + Copy, T2: HasValue + Copy> {
    lhs: T1,
    rhs: T2,
}

impl<T1: HasValue + Copy, T2: HasValue + Copy> HasValue for MulOp<T1, T2> {
    fn get_value(&self) -> i32 {
        self.lhs.get_value() * self.rhs.get_value()
    }
}

impl<T: HasValue + Copy, O: HasValue + Copy> Mul<O> for T {
    type Output = MulOp<T, O>;
    fn mul(&self, other: &O) -> MulOp<T, O> {
        MulOp {
            lhs: *self,
            rhs: *other,
        }
    }
}

fn main() {
    let a = Val { value: 1 };
    let b = Val { value: 2 };
    let c = Val { value: 2 };

    let e = ((a + b) * c).get_value();

    print!("{}", e);
}

想法?

【问题讨论】:

    标签: rust


    【解决方案1】:

    尝试为您的自定义类型定义特征Add,您正在这样做:

    impl<T: HasValue + Copy, O: HasValue + Copy> Add<O> for T {
        type Output = AddOp<T, O>;
        fn add(&self, other: &O) -> AddOp<T, O> {
            AddOp {
                lhs: *self,
                rhs: *other,
            }
        }
    }
    

    但是T: HasValue + Copy 匹配任何实现 trait HasValue 的类型,并且这种类型可能没有在您的 crate 中定义(例如,如果您为 i32 实现 HasValue)。由于 Add 也没有在您的 crate 中定义,Rust 抱怨:例如,通过为 i32 定义 HasValue,您还需要为 i32 重新定义 Add&lt;i32&gt;

    我的建议是将所有操作和值结构包装到一个通用结构中,并为此实现AddMul。这样,您只为您的 crate 中定义的简单类型实现 AddMul,编译器很高兴。

    类似的东西:

    struct Calculus<T> {
        calc: T,
    }
    
    impl<T: HasValue + Copy> HasValue for Calculus<T> {
        fn get_value(&self) -> i32 {
            self.calc.get_value()
        }
    }
    
    impl<T, O> Add<Calculus<O>> for Calculus<T>
    where
        T: HasValue + Copy,
        O: HasValue + Copy,
    {
        type Output = Calculus<AddOp<T, O>>;
        fn add(self, other: Calculus<O>) -> Calculus<AddOp<T, O>> {
            Calculus {
                calc: AddOp {
                    lhs: self.calc,
                    rhs: other.calc,
                },
            }
        }
    }
    
    impl<T, O> Mul<Calculus<O>> for Calculus<T>
    where
        T: HasValue + Copy,
        O: HasValue + Copy,
    {
        type Output = Calculus<MulOp<T, O>>;
        fn mul(self, other: Calculus<O>) -> Calculus<MulOp<T, O>> {
            Calculus {
                calc: MulOp {
                    lhs: self.calc,
                    rhs: other.calc,
                },
            }
        }
    }
    

    然后你可以为你的Val 类型添加一个简洁的new() 方法:

    impl Val {
        fn new(n: i32) -> Calculus<Val> {
            Calculus {
                calc: Val { value: n },
            }
        }
    }
    

    并像这样使用整个东西:

    fn main() {
        let a = Val::new(1);
        let b = Val::new(2);
        let c = Val::new(3);
    
        let e = ((a + b) * c).get_value();
    
        print!("{}", e);
    }
    

    Playground

    【讨论】:

    • 另一种解决方案是创建一个新特征,并让该特征同时实现HasValueCopy。然后可以为这个特征提供一个全面的Add 实现。
    • @MatthieuM。你确定吗 ?我看不出它是如何解决问题的,因为您仍然可以为已经存在的类型实现这个新特性。
    • 理论上,这是合理的,因为当为一个类型实现超特征时,您必须知道该类型是否已经实现了任何子特征(从而可以决定是否忽略超trait 默认实现或引发错误)。在实践中,我不知道 Rust 是否允许它,以及它是否选择了它选择的语义(忽略或错误)。
    猜你喜欢
    • 2022-10-24
    • 1970-01-01
    • 2021-01-01
    • 1970-01-01
    • 2021-02-20
    • 1970-01-01
    • 1970-01-01
    • 2017-01-13
    • 1970-01-01
    相关资源
    最近更新 更多