rust 闭包不是发送,尽管它只捕获发送变量

dvtswwa3  于 6个月前  发布在  其他
关注(0)|答案(1)|浏览(69)

我有下面的Rust代码(rustc 1.75.0),它“强制”一个类型为Send:

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。根据文档:
如果所有由非唯一不可变引用捕获的变量都是Sync,并且所有由唯一不可变或可变引用、复制或移动捕获的值都是Send,则闭包是Send。
然而,这无法编译,报告闭包是 not Send。
为什么这不编译?感谢任何帮助!

xcitsw88

xcitsw881#

wrapped.0只从wrapped中移动字段.0,该字段的类型为T(非发送)。这通常对partial moves很有用,因为您可以移动捕获字段的子集,并将其余字段保持为未移动。
在这种情况下,你必须确保你移动了 * 整个 * 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(https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=617068a47c564a7e93f36d9d0c788394)的
另外,如果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(https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=ee4c98c94dde25ad6cc42882ab5cc447)的

相关问题