发送特征的终身问题

时间:2014-11-06 16:37:37

标签: send rust lifetime

我很难理解为什么这段代码没有编译:

use std::cell::{Ref, RefCell};

struct St {
    data: RefCell<uint>
}

impl St {
    pub fn test(&self) -> Ref<uint> {
        self.data.borrow()
    }
}

// This code would compile without T constrained to be Send.
fn func<T: Send>(_: &T) {
}

fn main() {
    let s = St { data: RefCell::new(42) };

    {
        let r7 = s.test();
        // Do not compile
        func(&r7)
    }

    // Compile
    func(&s);
}

它出现以下错误:

bug.rs:21:18: 21:19 error: `s` does not live long enough
bug.rs:21         let r7 = s.test();
                           ^
note: reference must be valid for the static lifetime...
bug.rs:17:11: 28:2 note: ...but borrowed value is only valid for the block at 17:10
bug.rs:17 fn main() {
bug.rs:18     let s = St { data: RefCell::new(42) };
bug.rs:19
bug.rs:20     {
bug.rs:21         let r7 = s.test();
bug.rs:22         // Do not compile
          ...

当我尝试约束func()T特征兼容时,问题似乎出现在函数Send中。如果没有此约束,此代码将无错误地编译。

是否有人可以向我解释这种行为的原因是什么?

2 个答案:

答案 0 :(得分:11)

Rust 1.0的更新

在Rust 1.0及更高版本中,示例中的代码(当uint被某些现有类型替换时)失败并出现另一个错误:

% rustc test.rs
test.rs:23:9: 23:13 error: the trait `core::marker::Sync` is not implemented for the type `core::cell::UnsafeCell<usize>` [E0277]
test.rs:23         func(&r7)
                   ^~~~
test.rs:23:9: 23:13 help: run `rustc --explain E0277` to see a detailed explanation
test.rs:23:9: 23:13 note: `core::cell::UnsafeCell<usize>` cannot be shared between threads safely
test.rs:23:9: 23:13 note: required because it appears within the type `core::cell::Cell<usize>`
test.rs:23:9: 23:13 note: required because it appears within the type `core::cell::BorrowRef<'_>`
test.rs:23:9: 23:13 note: required because it appears within the type `core::cell::Ref<'_, i32>`
test.rs:23:9: 23:13 note: required by `func`

这有点棘手 - 另一个特质Sync,无处不在。

实现Send特征的类型(虽然它的文档目前缺乏,但是可以跨任务边界传输)。大多数类型都是Send,但有些类型(如RcWeak)不是Send,因为此类类型的实例可能共享非同步的可变状态,因此不安全从多个线程中使用。

在较旧的Rust版本Send中隐含'static,因此引用不是Send。但是,从Rust 1.0开始,Send不再暗示'static,因此可以跨线程发送引用。但是,为了&TSendT必须为Sync:以下实现需要这样做:

impl<'a, T> Send for &'a T where T: Sync + ?Sized

但在我们的情况下,我们并不要求&TSend,我们只要求TSend,所以它不应该真的很重要,对吗?

没有。事实上,仍有引用,即使我们没有立即看到它们。请记住,对于类型为Send,其组件必须为Send,即结构的每个字段和枚举的每个枚举变体的每个部分必须为Send struct / enum也是Sendcore::cell::Ref内部struct BorrowRef Cell<BorrowFlag>的实例,Sync &Cell<BorrowFlag>的引用Send。以下是Cell<BorrowFlag>的来源:按顺序或SyncSync,{{1}}必须为{{1}};但是,它不是也不能是{{1}},因为它提供了不同步的内部可变性。这是错误的实际原因。

答案 1 :(得分:2)

根据the Rust reference(强调我的):

  

Send :可以在任务之间安全地发送此类类型。这种类型包括标量,框,过程和仅包含其他所有类型的结构类型。 所有发送类型均为'static

实际上,如果你向其他任务发送了一些东西,你必须保证在其他任务完成使用之前它不会被销毁,所以它不能被当前任务拥有。

有两种方法可以确保它:

  • 完全拥有此对象(基本上,结构的所有成员都是Send)
  • 让您的对象进入静态存储

因此,通过要求函数的参数为​​Send,您需要r7'static,但它不能超过s(因为它是对'static的引用RefCell内容),它不是你在主要内容中定义的fn foo<T: 'a>(bar: T);

更一般地说,写作时

T

您需要'a

  • 一个类型,其所有生命周期参数都是&'a或更长(或没有参数)
  • 'a对类型本身T: Send的引用(您可以根据这些条件递交)

正如我们所见,T: 'static暗示{{1}}。