关于 rust:是否可以在闭包参数中”向下转换”一个特征对象?

Is it possible to 'downcast' a trait object in a closure argument?

我想调用一个函数指针,它本身将一个特征对象作为参数:

1
2
3
4
fn invoke(x: &Fn(&WithFoo), y: &MyStruct) {
    // MyStruct implements WithFoo
    x(y);
}

到目前为止一切顺利。现在,我遇到的问题是如何使用带有签名的函数指针调用它,例如 &Fn(&WithFooBar) 特征 WithFooBar 继承自 WithFoo.

这是我尝试执行此操作的示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
trait WithFoo {
    fn foo(&self);
}

trait WithFooBar: WithFoo {
    fn bar(&self);
}

struct MyStruct {
}

impl WithFoo for MyStruct {
    fn foo(&self) {
        println!("foo");
    }
}

impl WithFooBar for MyStruct {
    fn bar(&self) {
        println!("bar");
    }
}

fn foobar_caller(wf: &WithFooBar) {
    wf.foo();
    wf.bar();
}

fn invoke(x: &Fn(&WithFoo), y: &MyStruct) {
    x(y);
}

fn main() {
    let data = MyStruct {};
    invoke(&foobar_caller,&data);
}

这会失败并出现以下编译错误:

1
error: type mismatch: the type `fn(&WithFooBar) {foobar_caller}` implements the trait `for<'r> std::ops::Fn<(&'r WithFooBar + 'r,)>`, but the trait `for<'r> std::ops::Fn<(&'r WithFoo + 'r,)>` is required (expected trait `WithFoo`, found trait `WithFooBar`) [--explain E0281]

我知道错误是说 &Fn(&WithFooBar) 不是 &Fn(&WithFoo),但鉴于 WithFooBar 特征继承自 WithFoo,似乎应该可以将指针传递给该函数。

是否有可能以某种方式将函数指针"向下转换"为类型 &Fn(&WithFoo)?这两个我都试过了:

1
2
 let f = &foobar_caller as &Fn(&WithFoo);
 invoke(f,&data);

还有这个:

1
2
 let f: &Fn(&WithFoo) = &foobar_caller;
 invoke(f,&data);

但是这些尝试都不起作用。

(这个例子在 rust 的操场上。)


不,你不能这样做。首先,Rust 不支持将一个 trait 对象向下转换为另一个 trait 对象。使用 Any 特征,您可以将特征对象向下转换为底层对象的具体类型,然后获取不同类型的特征对象,但这需要知道对象的具体类型,而您不知道 - 这就是重点特征对象!

此外,您尝试做的事情并不合理:不应将期望接收 &WithFooBar 的函数传递给 &WithFoo,因为 WithFoo 可能无法实现 WithFooBar。如果反过来,那会是合理的,但是即使 WithFooBar 继承自 WithFoo,Rust 也不允许将 &WithFooBar 强制转换为 &WithFoo,所以即使我们尝试创建一个package函数,它不起作用。