【问题标题】:implementing traits for dyn Fns为 dyn Fns 实现特征
【发布时间】:2021-02-21 18:45:45
【问题描述】:

今天我在玩功能特征。虽然我在下面展示的示例实际上可能不是很有用,但我确实想知道为什么它不能编译。

pub fn do_something(o: &(dyn Other + 'static)) {

}

trait Other {
    fn do_something_other(&self);
}

impl<A> Other for dyn Fn(A) {
    fn do_something_other(&self) {
        do_something(self);
    }
}

这里我实现了一个函数类型的特征。此函数类型对其参数是通用的。这意味着如果您要这样做:

pub fn do_something(o: &(dyn Other + 'static)) {

}

trait Other {
    fn do_something_other(&self);
}

impl<F, A> Other for F where F: (Fn(A)) + 'static {
    fn do_something_other(&self) {
        do_something(self);
    }
}

您会收到一条错误消息,指出类型参数不受约束。

我明白这一点,但不相信使用泛型可以做到这一点。但是动态方法,为什么不起作用?它给出了以下错误:

我不明白这个错误。它声明我通过了一个Fn(A) -&gt; (),它没有实现Other。然而,这个错误确实发生在Other 的实现中。这里怎么不能实现?

我的第一个想法是因为每个闭包都有自己的类型。如果和这个有关,我觉得这个错误很奇怪。

【问题讨论】:

  • 为什么要在 dyn Fn 上实现 trait 而你可以在 Fn 上实现呢? impl&lt;F&gt; MyTrait for F where F: Fn(...)
  • 这正是我在问题的第二个示例中所解释的。你不能,因为这会给出“不受约束的生命周期参数”错误

标签: rust function-pointers traits


【解决方案1】:

第一个构造失败,因为您无法将&amp;dyn A 转换为&amp;dyn B,即使在为dyn A 实现B 时也是如此。

trait A {}

trait B {
    fn do_thing(&self);
}

impl B for dyn A {
    fn do_thing(&self) {
        let b: &dyn B = self;
    }
}
error[E0308]: mismatched types
 --> src/lib.rs:9:25
  |
9 |         let b: &dyn B = self;
  |                ------   ^^^^ expected trait `B`, found trait `A`
  |                |
  |                expected due to this
  |
  = note: expected reference `&dyn B`
             found reference `&(dyn A + 'static)`

好吧,您可以转换特征,但只能在源特征的帮助下。但是由于在这种情况下源是Fn,所以这不是路由。


第二个构造失败,因为 Rust 不允许你实现可能冲突的特征。尝试为实现 A&lt;_&gt; 的类型实现 B 将自动被拒绝,因为类型可以有多个 A&lt;_&gt; 实现。

trait A<T> {}

trait B {
    fn do_thing(&self);
}

impl<T, U> B for T where T: A<U> {
    fn do_thing(&self) {}
}
error[E0207]: the type parameter `U` is not constrained by the impl trait, self type, or predicates
 --> src/lib.rs:7:9
  |
7 | impl<T, U> B for T where T: A<U> {
  |         ^ unconstrained type parameter

特别是关于Fns,它有点难以分辨,因为通常函数对象只实现一个Fn trait。但是,关键字是通常,因为您可以在每晚启用一项功能来做到这一点。而且特质系统通常不受欢迎。


那你能做什么?好吧,第一种方法仍然是功能性的,只是你必须将实现保持在 trait 内。如果您对函数参数使用具体类型,则可以使用第二种方法。

您可以想象为&amp;dyn Fn(_) 实现Other(在引用而不是对象本身上实现它)。但这对于 Fn 对象的常用用法并不是特别方便。

pub fn do_something(o: &dyn Other) {}

trait Other {
    fn do_something_other(&self);
}

impl<A> Other for &dyn Fn(A) {
    fn do_something_other(&self) {
        do_something(self);
    }
}

fn main() {
    // THIS WORKS
    let closure: &dyn Fn(_) = &|x: i32| println!("x: {}", x);
    closure.do_something_other();
    
    // THIS DOESN'T WORK
    // let closure = |x: i32| println!("x: {}", x);
    // closure.do_something_other();
}

另一种选择是使Other trait 通用化以约束A,但这当然取决于它的设计使用方式。

【讨论】:

    猜你喜欢
    • 2020-05-09
    • 2019-06-17
    • 2020-10-08
    • 1970-01-01
    • 2019-12-08
    • 2021-11-14
    • 2022-11-04
    • 2015-08-07
    • 1970-01-01
    相关资源
    最近更新 更多