首页 > 解决方案 > 当闭包作为特征对象参数传递时,为什么需要强制转换?

问题描述

我正在尝试创建一个应该抽象出具有不同数量参数的函数/闭包的特征。像这样的东西:

trait MyTrait {}

impl MyTrait for Box<Fn() -> &'static str> {}

impl MyTrait for Box<Fn(u8) -> u8> {}

最初我打算这样使用它:

fn myf<F: MyTrait>(_fun: F) {}

fn main() {
    myf(Box::new(|i: u8| i + 2))
}

但是此代码失败并出现错误:

error[E0277]: the trait bound `std::boxed::Box<[closure@src/main.rs:11:18: 11:31]>: MyTrait` is not satisfied

当我像这样投射盒子时,一切都会正确编译:

myf(Box::new(|i: u8| i + 2) as Box<Fn(_) -> _>)

操场

为什么 Rust 编译器不能在没有强制转换的情况下推断出这个特征?我的方法(使用演员表)是否正确,还是有更简单的方法?我更喜欢为我的项目启用trivial_casts警告,这种语法会触发它。

标签: rustclosurestraits

解决方案


这是一个容易忘记的事情:每个闭包都是一个不同的结构,它实现Fn:Fn是一个特征,而不是一个结构,并且特征实现不是传递的。

这是一个说明这一点的小例子:

trait Base {}
trait Derived {}
struct Foo {}

impl Base for Derived {}

impl Derived for Foo {}

fn myf<T>(_t: Box<T>)
where
    T: Base + ?Sized,
{
}

fn main() {
    let foo = Box::new(Foo {});
    //myf(foo) // does not compile
    myf(foo as Box<Derived>)
}

你真正想做的是:

trait MyTrait {}

impl<T> MyTrait for T
where
    T: Fn() -> &'static str,
{
}

impl<T> MyTrait for T
where
    T: Fn(u8) -> u8,
{
}

fn myf<F>(_fun: Box<F>)
where
    F: MyTrait,
{
}

fn main() {
    myf(Box::new(|i: u8| i + 2))
}

但这无法编译,因为有两个相互冲突的实现。


推荐阅读