【问题标题】:Is there a way to implement a trait on top of another trait? [duplicate]有没有办法在另一个特征之上实现一个特征? [复制]
【发布时间】:2017-12-08 05:18:36
【问题描述】:

我正在尝试为我创建一个可以实现其他操作员特征(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`

【问题讨论】:

    标签: rust traits


    【解决方案1】:

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

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

    代替:

    impl Add for Measurement { ... }
    

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


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

    错误:类型参数T 必须用作某些类型的参数 本地类型(例如MyStruct&lt;T&gt;);仅在当前定义的特征 可以为类型参数实现 crate [E0210]

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

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

    最简单的解决方案是放弃并为您计划创建Unit 的每种类型独立实施 Add。假设你的 crate 定义了 InchesCentimeter,每个都有自己的 Add impl。如果代码非常相似,并且您觉得自己破坏了 DRY,请利用 macros。这里是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 }
        )*)
    }
    

    【讨论】:

    • 这是我刚刚到达的同一个地方。 T: Measurement 语法会很好,但我理解。感谢您提供宏的链接,如果我能够制作宏并通过最好的模块共享它。
    【解决方案2】:

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

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

    不幸的是,您只能对 your crate 中定义的特征执行此操作(称为一致性),因此您不能对 std Add 特征执行此操作,因为它是在 std crate 中定义的,而不是在你的。

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

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-05-29
        • 2019-09-12
        • 2017-03-24
        • 2018-12-16
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多