方法1(工程):

pub fn spawn_good<F, R>(future: F) -> JoinHandle<R>
where
    F: Future<Output = R> + 'static + Send,
    R: Send + 'static,
{
    let (sender, recvr) = oneshot::channel();
    let future = async move {
        sender.send(future.await);
    };

    let task = Arc::new(Task {
        state: AtomicUsize::new(0),
        future: Mutex::new(Box::pin(future)),
    });

    Box::pin(async { recvr.await.unwrap() })
}

方法2(不起作用):

pub fn spawn_bad<F, R>(future: F) -> JoinHandle<R>
where
    F: Future<Output = R> + 'static + Send,
    R: Send + 'static,
{
    let (sender, recvr) = oneshot::channel();
    let future = async move {
        sender.send(future.await);
    };

    let future = Mutex::new(Box::pin(future));

    let task = Arc::new(Task {
        state: AtomicUsize::new(0),
        future,
    });

    Box::pin(async { recvr.await.unwrap() })
}
expected struct `std::sync::Mutex<Pin<Box<(dyn futures::Future<Output = ()> + std::marker::Send + 'static)>>>`
   found struct `std::sync::Mutex<Pin<Box<impl futures::Future<Output = [async output]>>>>`

任务定义:

struct Task {
    state: AtomicUsize,
    future: Mutex<Pin<Box<dyn Future<Output = ()> + Send>>>
}

JoinHandle定义:

type JoinHandle<T> = Pin<Box<dyn Future<Output = T> + Send>>;

我不明白为什么第二种方法行不通.唯一的区别是额外的分配.我错过的Mutex个有什么细微差别吗?

推荐答案

这类似于this question.当您将future指定给新创建的Task时,由于Task::future的类型为Mutex<Pin<Box<dyn Future<Output = ()> + Send>>>,这将成为类型推断的约束:future应该具有Mutex<Pin<Box<dyn Future<Output = ()> + Send>>>类型.

现在,future实际上有Mutex<Pin<Box<[some future generated by async block]>>>型(从现在起我们将其命名为[some future generated by async block] Fut),not Mutex<Pin<Box<dyn Future<Output = ()> + Send>>>.Pin<Box<Fut>>可以coercePin<Box<dyn Future<Output = ()> + Send>>(Box<Fut>也可以强制Box<dyn Future<Output = ()> + Send>),但只有在编译器认为有必要时才会这样做.另一方面,Mutex<Pin<Box<Fut>>> cannot强制到Mutex<Pin<Box<dyn Future<Output = ()> + Send>>>,因为强制不是递归的(T可以强制到U并不意味着A<T>可以强制到A<U>).

重要的一点是,编译器只判断是否应在特殊coercion sites执行强制. struct 的实例化是每个字段表达式的强制站点,方法调用也是,但没有显式类型的变量声明let语句不是强制站点.我们可以遵循编译器推断:

对于第一个工作 case :

Task::future提供的表达式应具有Mutex<Pin<Box<dyn Future<Output = ()> + Send>>>类型,即这是它的expected type(或者,编译器具有100以使其属于该类型).

由于Mutex::new()具有签名fn<T>(T) -> Mutex<T>,并且我们知道它应该返回Mutex<Pin<Box<dyn Future<Output = ()> + Send>>>,因此我们可以得出T的值是Pin<Box<dyn Future<Output = ()> + Send>>的结论.因此,它的参数也应该具有该类型,或者这是它的预期类型.

Box::pin()同上,签名fn<T>(T) -> Pin<Box<T>>.现在Tdyn Future<Output = ()> + Send.我们不提供该类型的值:我们提供Fut类型的值.但不要放弃!让我们追溯并try 找到一个好的强制.Fut不能直接强制到dyn Future<Output = ()> + Send,但如果我们返回一步,我们会看到Pin<Box<Fut>>can被强制到Pin<Box<dyn Future<Output = ()> + Send>>.这在方法调用(Mutex::new())中,因此可以在此处强制它.问题已解决.

现在让我们来看看第二种情况:

首先,我们推断future变量的类型.我们对它没有期望,因为没有指定类型.我们也不期望Mutex::new()分之TBox::pin()分之一.所以我们可以从Box::pin()TFut,然后Mutex::new()TPin<Box<Fut>>的值得出结论.因此,future具有Mutex<Pin<Box<Fut>>>型.

现在,当try 对字段future的表达式进行类型判断时,我们注意到它的类型为Mutex<Pin<Box<Fut>>>,而预期的类型为Mutex<Pin<Box<dyn Future<Output = ()> + Send>>>.我们不能将第一个强制到第二个,但也不能传播强制,因为let不是强制站点.所以我们声明一个错误.

修复方法很简单:您应该插入一个强制站点.您可以指定let future的类型,并让编译器强制它,或者在Box::pin()之后指定as _(Mutex::new(Box::pin(future) as _),当然指定类型也可以),强制编译器在那里放置一个强制站点(这很令人困惑,因为方法调用已经是强制站点,但默认情况下编译器不会在那里强制,因为它通过参数推断出T.在那里放置as _就像告诉编译器"停止!我想断开返回类型与参数类型的连接,它不一定是Pin<Box<[parameter type]>>,它只应该是可强制的(准确地说,是可浇铸的)它!").

Rust相关问答推荐

在使用AWS SDK for Rust时,如何使用硬编码访问密钥ID和密钥凭据?

什么时候和为什么S最好是按值或引用传递简单类型

写入引用会更新基础值,但引用会打印意外的值

装箱特性如何影响传递给它的参数的生命周期 ?(举一个非常具体的例子)

为相同特征的特征对象使用 move 方法实现特征

如何对一个特征的两个实现进行单元测试?

为什么我可以使用 &mut (**ref) 创建两个实时 &mut 到同一个变量?

需要一个有序向量来进行 struct 初始化

Rust 中的内存管理

trait 对象指针的生命周期

Rust typestate 模式:实现多个状态?

类型判断模式匹配panic

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

在 Rust 中如何将值推送到枚举 struct 内的 vec?

预期的整数,找到 `&{integer}`

在 Rust 中退出进程

制作嵌套迭代器的迭代器

如果返回类型是通用的,我可以返回 &str 输入的一部分吗?

如何在不设置精度的情况下打印浮点数时保持尾随零?

当引用不再被borrow 时,Rust 不会得到它