Box inverse traits / Where子句在框中

问题描述 投票:1回答:1

我遇到了无法在Boxes和其他通用结构中使用where子句的问题。这对于通用通信添加很有用。 (A + B = B + A)。我正在努力实现的一个例子:

enum Foo<T> {
    Bar(Box<U where T: Add<U, Output = T>>),
    Baz(Box<U where T: Sub<U, Output = T>>),
}

这可以使用类型定义中的where子句半成品:

enum FooHack<T, A, S>
where
    A: Add<T, Output = T>,
    S: Sub<T, Output = T>,
{
    Bar(A),
    Baz(S),
    Dummy(PhantomData<T>), // to stop the compiler from complaining
}

FooHack的类型签名要求AS是具体类型。是否有策略使AS可以通用?

generics rust
1个回答
2
投票

也许你已经知道trait objects了?这与你的目的完全匹配,让U动态改变固定的T

但是,有两个问题阻止直接使用AddSub作为特征对象:

  1. AddSub不是对象安全的,因为它期望一个按值的self论证。
  2. 特质对象在Self上是存在的,但你想要的特征对象存在于RHS / AddSub上。

因此,您将不得不做额外的工作来介绍中间特征:

use std::ops::Add;

trait MyAdd<T> {
    fn my_add(&self, other: T) -> T;
}

impl<T, U> MyAdd<T> for U
where
    T: Add<U, Output = T>,
    U: Clone,
{
    fn my_add(&self, other: T) -> T {
        other + self.clone()
    }
}

enum Foo<T> {
    Bar(Box<MyAdd<T>>),
}

如果您不想要U: Clone,可以根据您的使用情况选择:

  • 假设T: Add<&'a U, Output = T>而不是T: Add<U, Output = T>
  • self: Box<Self>中使用&self而不是fn my_add

Edit: operator overloading

上面的代理特征MyAdd<T>本身不代理运算符重载;因此你必须为此手动实现std::ops::Add

// Coherence error
impl<T> Add<Box<MyAdd<T>>> for T {
    type Output = T;
    fn add(self, other: Box<MyAdd<T>>) -> Self::Output {
        other.my_add(self)
    }
}

但是,由于一致性限制,此代码不起作用。这种限制是至关重要的,因为兄弟板条箱总是有可能像impl<T> Add<T> for TypeDefinedInTheSiblingCrate一样,它肯定会与上面的impl重叠。

一种可能的解决方法是使用the newtype pattern

struct Wrapper<T>(T);

impl<T> Add<Box<MyAdd<T>>> for Wrapper<T> {
    type Output = Wrapper<T>;
    fn add(self, other: Box<MyAdd<T>>) -> Self::Output {
        Wrapper(other.my_add(self.0))
    }
}

然后你可以写像Wrapper(x) + boxed_myadd_value + another_myadd_value这样的东西。

© www.soinside.com 2019 - 2024. All rights reserved.