有没有办法在另一个特征之上实现一个特征? [英] Is there a way to implement a trait on top of another trait?

查看:47
本文介绍了有没有办法在另一个特征之上实现一个特征?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am trying to create a base trait that will implement other operator traits (Add, Subtract, Multiply, Divide, etc...) for me.

这编译失败,看起来像是用 Sized 发出的,但即使 Measurement 设置为 require Sized 它也不起作用.这甚至可能吗?

This fails to compile, it looks like an issued with Sized, but even when Measurement is set to require Sized it does not work. Is this even possible?

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

(游乐场)

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`

推荐答案

问题不在于 Sized.您正在寻找的语法是:

The issue is not with Sized. The syntax you're looking for is:

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

代替:

impl Add for Measurement { ... }

因为 for 的右侧必须是一个对象,而不是一个 trait,而是一个被约束到一个 trait 的类型参数(即需要一个 T测量) 有效.

Because the right-hand side of the for must be an object, not a trait, however a type parameter constrained to a trait (i.e. a T required to be Measurement) is valid.

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

Now your code still won't compile. You will get the following:

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

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 的实现写入任何 TMeasurement 时,您打开了一个类型已经实现了 Add 本身,也会在其他地方实现 Measurement.想象一下,如果你想在 u8 上实现 Measurement(这很愚蠢但可能):Rust 应该为 Add?原始的 std impl 还是您的 Measurement impl?(对此问题的深入讨论)

The issue here is of a totally different kind. I'm not sure it's related to the question any more but I'll still explain what's going on. When you write an impl for Add to any T which is Measurement, you open the possibility that a type would already implement Add on its own, and would also implement Measurement elsewhere. Imagine if you wanted to implement Measurement on u8 (which is silly but possible): which impl should Rust choose for Add? The original std impl or your Measurement impl? (in-depth discussion about this issue)

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

Right now Rust plainly forbids an impl if it is not at least 1) your own trait or 2) your own type (where "own" formally means, in the crate you're writing your impl). This is why you can write impl Add for Unit: because you own Unit.

最简单的解决方案是放弃并为您计划制作 Unit 的每种类型独立实现 Add.假设您的 crate 定义了 InchesCentimeter,每个都有自己的 Add 实现.如果代码非常相似,并且您觉得自己搞砸了 DRY,请利用 .这是 std 箱的方式是吗:

The easiest solution would be to give up and implement Add independently for each type you're planning to make Unit. Say your crate defines Inches and Centimeter, each one would have its own Add impl. If the code is insultingly similar, and you feel you broke DRY, leverage 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 }
    )*)
}

这篇关于有没有办法在另一个特征之上实现一个特征?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆