3

在使用盒装闭包时,我遇到了以下问题:

type Test = Rc<dyn Fn() -> i64>;

fn test_bad() -> Test {
    Test::new(|| 42)
}

fn test_good() -> Test {
    Rc::new(|| 42)
}

在第一种情况下,我使用类型别名来引用方法,而在第二种情况下new我直接使用。Rc

在第一种情况下,编译器抱怨:

    |       Test::new(|| 42)
    |             ^^^ function or associated item not found in `Rc<(dyn Fn() -> i64 + 'static)>`
    |
    = note: the method `new` exists but the following trait bounds were not satisfied:
            `dyn Fn() -> i64: Sized`

但第二种情况效果很好。有人可以解释一下区别吗?有什么方法可以new通过类型别名引用还是我需要自己包装它?

4

2 回答 2

4

test_good中,通过调用Rc::newon || 42,您不会创建一个Rc<dyn Fn() -> i64>,而是创建一个Rc<ClosureType>,其中提供的闭包ClosureType唯一类型是,它大小的。然后,由于在Rc<ClosureType>返回的函数中返回Rc<dyn Fn() -> i64>,因此它隐式地将其强制转换为特征对象。

test_bad失败是因为它没有从构造Rc一个大小的闭包然后将其转换为一个大小不一Rc的特征对象,而是尝试直接构造一个Rc大小不一的特征对象,这失败了,因为函数的所有参数都必须调整大小。

我认为没有直接的方法可以new通过类型别名进行引用,尽管您可以很容易地制作自己的:

fn new_test(func: impl 'static + Fn() -> i64) -> Test {
    Rc::new(func)
}
于 2021-02-14T12:54:45.407 回答
3

泛型类型别名有效,但具体类型别名失败:

use std::rc::Rc;

type MyGenericRc<T> = Rc<T>;

// works
fn construct_with_generic_alias() -> MyGenericRc<dyn Fn() -> i64> {
    MyGenericRc::new(|| 42)
}

type MyConcreteRc = Rc<dyn Fn() -> i64>;

// fails
fn construct_with_concrete_alias() -> MyConcreteRc {
    MyConcreteRc::new(|| 42)
}

当您调用MyGenericRc::new(<closure>)编译器选择Rc::<closure type>::new(<closure>)作为要使用的具体函数实现时,由于Rc::new需要一个Sized参数并且每个匿名闭包类型都是Sized. 但是,在调用该函数之后,编译器会执行大小调整强制转换以将返回的值Rc<anonymous closure type>转换为Rc<dyn Fn() -> i64>.

当你打电话时,MyConcreteRc::new(<closure>)你不要让编译器决定Rc::new使用哪个实现,你告诉它它必须使用Rc::<dyn Fn() -> i64>::new但不可能满足该实现的类型要求,因为new需要一个Sized参数 but dyn Fn() -> i64is !Sized,因此你看到的编译错误消息。

于 2021-02-14T13:03:20.227 回答