我现在对如何协调Rust中的某个设计模式感到困惑.该模式涉及一个具有一般功能的特征,该特征基于某个标记特征而受到约束.For the sake of this question assume it is impossible to modify 100 or 101.我已经知道do_foo应该被放在一个单独的性状中,而不是使用标记性状,但我不是那个做出决定的人.

trait Foo {
    fn do_foo<T>(&mut self, _: &T) where Self: FooMarker<T>;
    fn do_bar(&mut self);
}

/// Indicates that a specific type is supported for doing Foo.
trait FooMarker<T>: Foo {}

乍一看,这看起来很合理,但试图以通用的方式使用这种模式几乎是不可能的.例如,以通常微不足道的包装器 struct 扩展Foo的功能为例.最初,我认为这个实现可能会像预期的那样工作,但它遇到了许多问题.

struct FooWrapper<F> {
    inner_foo: F
}

impl<F: Foo> Foo for FooWrapper<F> {
    fn do_foo<T>(&mut self, x: &T) where Self: FooMarker<T> {
        do_extra_stuff();
        self.inner_foo.do_foo(x)
    }

    fn do_bar(&mut self) {
        self.inner_foo.do_bar();
    }
}

impl<T, F: FooMarker<T>> FooMarker<T> for FooWrapper<F> {}

Rust Playground
Conceptually we may be able to look at this and know that FooWrapper<F>: FooMarker<T> implies F: FooMarker<T>, but the compiler does not want to rely on this information. After thinking for a second, this kinda makes sense. Nowhere in impl Foo for FooWrapper do we require that F: FooMarker<T>.

How can I write 100 without rewriting 101 or 102?

推荐答案

恐怕你不能,除非我遗漏了什么.

impl<T, F: FooMarker<T>> FooMarker<T> for FooWrapper<F> {}告诉我们,如果F实现FooMarker<T>,那么FooWrapper<F>也实现FooMarker<T>.但事实并非如此,就像下面这样:

struct Bar;
struct Baz;

impl Foo for FooWrapper<Bar> {
    fn do_foo<T>(&mut self, _: &T)
    where
        Self: FooMarker<T>
    {}
}

impl FooMarker<Baz> for FooWrapper<Bar> {}

我们也不能改变where%的条款,因为它是在Foo的宣言中定义的.


对我来说,真正的解决方案是让Foo本身成为通用的,我理解你正在考虑的:

trait Foo<T> {
   fn do_foo(&mut self, _: &T);
}

坦率地说,我从来没有见过这样的"标记"模式(除了可能涉及封闭特征的上下文,不确定),我看不出它有什么帮助.

Rust相关问答推荐

如果A == B,则将Rc A下推到Rc B

将大小为零的类型实例存储到空指针中

在Rust中,如果Result是Err,运行副作用(如日志(log)记录)的惯用方法是什么

使用Box优化可选的已知长度数组的内存分配

Rust&;Tokio:如何处理更多的信号,而不仅仅是SIGINT,即SIGQUE?

如何将单个 struct 实例与插入器一起传递到Rust中的映射

Rust将String上的迭代器转换为&;[&;str]

返回Result<;(),框<;dyn错误>>;工作

对reqwest提供的这种嵌套JSON struct 进行反序列化

为什么不';t(&;mut-iter).take(n)取得iter的所有权?

为什么 GAT、生命周期和异步的这种组合需要 `T: 'static`?

在 Rust 中,是否可以定义一个需要实现类型的构造函数的对象安全特征?

如何以与平台无关的方式将OsString转换为utf-8编码的字符串?

unwrap 选项类型出现错误:无法移出共享引用后面的*foo

如何在 Rust 中将枚举变体转换为 u8?

如果不满足条件,如何在 Rust 中引发错误

无法理解 Rust 对临时值的不可变和可变引用是如何被删除的

为什么允许重新分配 String 而不是 *&String

RAII 模式的 Rust 解决方案,用于在 new() 和 drop() 上修改另一个对象

编写 TOML 文件以反序列化为 struct 中的枚举