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

时间:2015-06-26 21:08:12

标签: rust traits

我正在尝试创建一个基本特征,它将为我实现其他操作员特征(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 个答案:

答案 0 :(得分:7)

问题不在于大小。您正在寻找的语法是:

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

而不是:

impl Add for Measurement { ... }

因为for的右侧必须是对象而不是特征,但是类型参数必须是特性(即T必须是Measurement)是有效的。

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

  

错误:类型参数T必须用作某些类型参数   本地类型(例如MyStruct<T>);只有当前定义的特征   crate可以用于类型参数[E0210]

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

现在Rust显然禁止impl如果它不是至少1)你自己的特性或2)你自己的类型(其中&#34;拥有&#34;正式意味着,在箱子里你正在写你的IMPL)。这就是为什么你可以写Measurement:因为你拥有impl Add for Unit

最简单的解决方案是为您计划制作Unit的每种类型单独放弃并实施添加。说你的箱子定义UnitInches,每个人都有自己的Centimeter impl。如果代码侮辱性地相似,并且你觉得你破坏了干,请利用macros。这是how the std crate does it

Add

答案 1 :(得分:0)

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

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中定义的特征(它叫做coherence)这样做,所以你不能为std Add特征做到这一点,因为它是在std crate中定义的,不在你的身上。

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

答案 2 :(得分:0)

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

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