有没有一种方法可以在另一个trait上实现一个trait?

14

我正在尝试创建一个基础特质,用于实现其他操作特质(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`
3个回答

12
问题不在于Sized。你需要的语法是:
```rust let array: [i32; size] = [0; size]; ```
其中,`size`是数组大小,可以是任意整数值。
impl<T: Measurement> Add for T { ... }

改为:

impl Add for Measurement { ... }

因为for的右边必须是一个对象,而不是一个trait,但是一个约束到trait的类型参数(即需要MeasurementT)是有效的。
现在你的代码仍无法编译。你将会得到以下错误:

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]

这里的问题是完全不同的。我不确定这是否还与问题有关,但我仍然会解释发生了什么。当你为任何实现了MeasurementT实现Add时,你打开了一种可能性,即某个类型已经自己实现了Add,并且在其他地方也实现了Measurement。想象一下,如果你想在u8上实现Measurement(这很愚蠢,但是可能),Rust应该选择哪个Add实现?原始的std实现还是你的Measurement实现?(关于这个问题的深入讨论)。
现在,如果实现不是至少1)你自己的trait或2)你自己的类型(其中“自己”正式意味着,在编写impl的crate中),Rust将直接禁止实现。这就是为什么你可以编写impl Add for Unit:因为你拥有Unit
最简单的解决方法是放弃,并独立地为你计划制作成Unit的每种类型实现Add。比如说,你的crate定义了InchesCentimeter,那么每一个都将有自己的Add实现。如果代码非常相似,而且你觉得你违反了DRY原则,请利用。下面是std crate使用的方式
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 }
    )*)
}

这是我刚到达的地方。T: Measurement 语法会很好,但我理解。感谢提供宏链接,如果我能制作一个宏并通过模块共享,那就太好了。 - jocull

3
您不能为trait实现一个trait,只能为类型实现trait。 但是您可以为实现了某些特定traits(trait bounds)的泛型类型实现trait。 像这样:
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)
    }
}

很遗憾,你只能为你的 crate 中定义的 trait(称为一致性)执行此操作,因此你不能为 std crate 中定义的 Add trait 执行该操作,因为它不是在你的 crate 中定义的。

我认为你可能需要定义一些 来实现你想要的功能。


1

这是一个带有宏的工作版本,如建议所示:

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());
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接