有没有办法在另一个特征之上实现一个特征?

Is there a way to implement a trait on top of another trait?

我正在尝试创建一个基本特征来实现其他运算符特征(AddSubtractMultiplyDivide 等...)我.

编译失败,看起来像是 Sized 的问题,但即使 Measurement 设置为需要 Sized,它也不起作用。这可能吗?

use std::ops::Add;

#[derive(Copy, Clone, Debug)]
struct Unit {
    value: f64,
}

impl Unit {
    fn new(value: f64) -> Unit {
        Unit { value: value }
    }
}

trait Measurement: Sized {
    fn get_value(&self) -> f64;
    fn from_value(value: f64) -> Self;
}

impl Measurement for Unit {
    fn get_value(&self) -> f64 {
        self.value
    }
    fn from_value(value: f64) -> Self {
        Unit::new(value)
    }
}

// This explicit implementation works
/*
impl Add for Unit {
    type Output = Unit;

    fn add(self, rhs: Unit) -> Unit {
        let a = self.get_value();
        let b = rhs.get_value();
        Unit::from_value(a + b)
    }
}
*/

// This trait implementation does not
impl Add for Measurement {
    type Output = Self;

    fn add(self, rhs: Self) -> Self {
        let a = self.get_value();
        let b = rhs.get_value();
        Self::from_value(a + b)
    }
}

fn main() {
    let a = Unit::new(1.5);
    let b = Unit::new(2.0);
    let c = a + b;

    println!("{}", c.get_value());
}

(playground)

error[E0277]: the trait bound `Measurement + 'static: std::marker::Sized` is not satisfied
  --> src/main.rs:42:6
   |
42 | impl Add for Measurement {
   |      ^^^ `Measurement + 'static` does not have a constant size known at compile-time
   |
   = help: the trait `std::marker::Sized` is not implemented for `Measurement + 'static`

error[E0038]: the trait `Measurement` cannot be made into an object
  --> src/main.rs:42:6
   |
42 | impl Add for Measurement {
   |      ^^^ the trait `Measurement` cannot be made into an object
   |
   = note: the trait cannot require that `Self : Sized`

error[E0038]: the trait `Measurement` cannot be made into an object
  --> src/main.rs:43:5
   |
43 |     type Output = Self;
   |     ^^^^^^^^^^^^^^^^^^^ the trait `Measurement` cannot be made into an object
   |
   = note: the trait cannot require that `Self : Sized`

error[E0038]: the trait `Measurement` cannot be made into an object
  --> src/main.rs:45:5
   |
45 |     fn add(self, rhs: Self) -> Self {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Measurement` cannot be made into an object
   |
   = note: the trait cannot require that `Self : Sized`

不能为特征实现特征,只能为类型实现特征。 但是您可以为实现特定特征(特征边界)的泛型类型实现特征。 像这样:

impl<T : Measurement> Add<T> for T {
    type Output = T;

    fn add(self, rhs: Self) -> T {
        let a = self.get_value();
        let b = rhs.get_value();
        T::from_value(a + b)
    }
}

不幸的是,您只能对 您的 板条箱中定义的特征执行此操作(称为连贯性),因此您不能对 std Add 特征执行此操作,因为它已定义在 std 板条箱中,而不是在您的板条箱中。

我想你可能需要定义一些来做你想做的事。

问题不在于 Sized。您要查找的语法是:

impl<T: Measurement> Add for T { ... }

而不是:

impl Add for Measurement { ... }

因为 for 的右边必须是一个对象,而不是一个特征,而是一个类型参数约束到一个特征(即 T 必须是 Measurement) 有效。


现在您的代码仍然无法编译。您将获得以下内容:

error: type parameter T must be used as the type parameter for some local type (e.g. MyStruct<T>); only traits defined in the current crate can be implemented for a type parameter [E0210]

这里的问题完全不同。我不确定它是否与问题有关,但我仍然会解释发生了什么。当您将 Add 的实现写入任何 MeasurementT 时,您打开了一个类型已经实现了 Add 的可能性,并且还将实现 Measurement 别处。想象一下,如果你想在 u8 上实现 Measurement(这很愚蠢但是 可能 ):Rust 应该为 Add 选择哪个实现?原来的 std impl 还是你的 Measurement impl? (in-depth discussion about this issue)

现在 Rust 明确禁止 impl,如果它不是至少 1) 你自己的特征或 2) 你自己的类型("own" 正式意味着,在你正在编写你的 impl 的箱子里)。这就是为什么你可以写 impl Add for Unit: 因为你拥有 Unit.

最简单的解决方案是放弃并为您计划制作的每种类型独立实施添加 Unit。假设你的 crate 定义了 InchesCentimeter,每个都有自己的 Add impl。如果代码非常相似,并且您觉得自己已经搞砸了,请利用 macros. Here is how the std crate does it:

macro_rules! add_impl {
    ($($t:ty)*) => ($(
        #[stable(feature = "rust1", since = "1.0.0")]
        impl Add for $t {
            type Output = $t;

            #[inline]
            fn add(self, other: $t) -> $t { self + other }
        }

        forward_ref_binop! { impl Add, add for $t, $t }
    )*)
}

根据建议,这是一个带有宏的工作版本:

use std::ops::Add;

#[derive(Copy, Clone, Debug)]
struct Unit {
    value: f64,
}

impl Unit {
    fn new(value: f64) -> Unit {
        Unit { value: value }
    }
}

trait Measurement: Sized {
    fn get_value(&self) -> f64;
    fn from_value(value: f64) -> Self;
}

impl Measurement for Unit {
    fn get_value(&self) -> f64 {
        self.value
    }
    fn from_value(value: f64) -> Self {
        Unit::new(value)
    }
}

macro_rules! add_impl {
    ($($t:ty)*) => ($(
        impl Add for $t {
            type Output = $t;

            fn add(self, other: $t) -> $t {
                let a = self.get_value();
                let b = other.get_value();
                let r = a + b;
                Self::from_value(r)
            }
        }
    )*)
}

add_impl! { Unit }

fn main() {
    let a = Unit::new(1.5);
    let b = Unit::new(2.0);
    let c = a + b;

    println!("{}", c.get_value());
}