我有以下Rust代码(Rustc 1.75.0),它"强制"发送一个类型:

struct ForceSend<T>(T);
unsafe impl<T> Send for ForceSend<T> {}

pub fn perform<T>(t: T) {
    let wrapped = ForceSend(t);
    takes_send(move || {
        wrapped.0;
    });
}

fn takes_send<F: Send>(_: F) {}

我希望这段代码能够编译,因为闭包通过Move捕获了一个值wrapped,并且这个值是Send.每the documentation人:

如果由非唯一不可变引用捕获的所有变量都是同步的,且通过唯一不可变或可变引用、复制或移动捕获的所有值都被发送,则发送闭包.

然而,这无法编译,报告关闭是not发送.

为什么这不能编译?谢谢你的帮助!

推荐答案

wrapped.0仅将字段.0wrapped移出,并且该字段的类型为T(非发送).这通常对partial moves很有用,您可以在其中移动-捕获字段的子集,并保持其余字段不移动.

在这种情况下,您必须确保移动entire wrapped对象,例如,通过创建将整个Wrapped移动到闭包中的绑定:

struct ForceSend<T>(T);
unsafe impl<T> Send for ForceSend<T> {}

pub fn perform<T>(t: T) {
    let wrapped = ForceSend(t);
    takes_send(move || {
        let w = wrapped;
        w.0;
    });
}

fn takes_send<F: Send>(_: F) {}

[playground]

在不相关的说明中,如果T确实是不受限制的,则此代码可能是不可靠的,并且如果takes_send确实想要跨线程发送东西,则可能导致UB.例如,如果互斥/相关的某些操作系统实现从不同于获取线程的线程释放,则会发生故障,并且跨线程发送MutexGuard可能会产生这种情况.


另外,在更有用的情况下,下面是捕获过程中的部分移动行为的示例:

pub struct Foo(String, String);

pub fn perform(t: Foo) {
    takes_closure(move || {
        t.0
    });
    
    takes_closure(move || {
        t.1
    });
}

fn takes_closure<F: FnOnce() -> String>(_: F) {}

[playground]

Rust相关问答推荐

是否可以为`T:Copy`执行`T. clone`的测试

从Rust调用C++虚拟方法即使在成功执行之后也会引发Access违规错误

交叉术语未正确清除屏幕

我怎样才能从一个Rust 的日期中go 掉3年?

MPSC频道在接收器处阻塞

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

为什么我们需要std::thread::scope,如果我们可以使用thread.join()在函数的生命周期内删除引用?

Rust 中什么时候可以返回函数生成的字符串切片&str?

这个不安全的 Rust 代码有什么问题,所以它可以在 Windows 上运行,但不能在 Ubuntu 上运行?

仅在使用 &mut 或线程时borrow 的数据在闭包之外转义?

为什么我们有两种方法来包含 serde_derive?

Rust 中 Mutex<> 的深拷贝?

在 Rust 中使用 `SecTrustSettingsSetTrustSettings` 绑定导致 `errSecInternalComponent`

内部值发生变化时 Rc 的行为

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

Rustlings 切片原语

有没有更好的方法来为拥有 DIsplay 事物集合的 struct 实现 Display?

如何从 Rust 中不同类型的多个部分加入 Path?

如何迭代调用可能会失败的函数?操作员?

守卫如何影响匹配语句?