2014-11-06 24 views
5

Tôi có khó khăn tìm hiểu lý do tại sao mã này không biên dịch:vấn đề Lifetime với Send đặc điểm

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); 
} 

Nó cung cấp cho các lỗi sau:

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 
      ... 

Vấn đề này có vẻ là trong hàm func() khi tôi cố gắng hạn chế T để tương thích với đặc điểm Send. Không có ràng buộc này, mã này biên dịch mà không có lỗi.

Có ai có thể giải thích cho tôi lý do của hành vi này là gì không?

Trả lời

10

Cập nhật cho Rust 1.0

Trong Rust 1.0 và sau đó là mã trong ví dụ (khi uint s được thay thế bằng một số loại hiện có) không thành công với một lỗi khác:

% 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` 

Đây là kinda khó khăn - một đặc điểm khác, Sync, đã xuất hiện từ hư không.

Một loại thực hiện Send đặc điểm (mặc dù tài liệu của nó chắc chắn là thiếu bây giờ) là một thứ có thể được chuyển qua các ranh giới nhiệm vụ. Hầu hết các loại là Send, nhưng một số, như RcWeak, không phải là Send vì các trường hợp loại như vậy có thể chia sẻ trạng thái không thể đồng bộ hóa và do đó không an toàn để sử dụng từ nhiều chuỗi.

Trong phiên bản cũ hơn Rust Send ngụ ý 'static, vì vậy tài liệu tham khảo không phải là Send. Vì Rust 1.0, tuy nhiên, Send không còn ngụ ý 'static, do đó, các tham chiếu có thể được gửi qua các chuỗi. Tuy nhiên, để cho &TSend, T phải Sync: đây là yêu cầu của việc thực hiện sau đây:

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

Nhưng trong trường hợp của chúng tôi chúng tôi không đòi hỏi rằng &TSend, chúng tôi chỉ yêu cầu TSend, vì vậy nó không thực sự quan trọng, phải không?

Không. Thực tế, vẫn còn là tham chiếu, ngay cả khi chúng tôi không thấy chúng ngay lập tức. Hãy nhớ rằng, đối với loại là Send, mỗi thành phần của nó phải là Send, tức là mỗi trường của cấu trúc và mỗi phần của mỗi biến thể enum của một enum phải là Send cho cấu trúc này/enum là Send. core::cell::Ref nội bộ contains một phiên bản của struct BorrowRef, lần lượt contains tham chiếu đến Cell<BorrowFlag>. Và đây là nơi Sync đến từ: theo thứ tự hoặc &Cell<BorrowFlag>Send, Cell<BorrowFlag> phải là Sync; tuy nhiên, nó không phải là và không thể là Sync vì nó cung cấp khả năng đột biến nội bộ không đồng bộ. Đây là nguyên nhân thực sự gây ra lỗi.

+0

Cảm ơn câu trả lời tuyệt vời, tôi hiểu ngay bây giờ! – user3762625

2

Theo the Rust reference (tôi nhấn mạnh):

Send : Types of this kind can be safely sent between tasks. This kind includes scalars, boxes, procs, and structural types containing only other owned types. All Send types are 'static .

Thật vậy, nếu bạn gửi một cái gì đó cho một nhiệm vụ khác, bạn phải đảm bảo rằng nó sẽ không bị phá hủy trước khi nhiệm vụ này khác đã hoàn tất sử dụng nó, vì vậy nó không thể được sở hữu bởi nhiệm vụ hiện tại.

Có hai cách để đảm bảo nó:

  • Có đối tượng này được sở hữu đầy đủ (về cơ bản, tất cả các thành viên của struct của bạn đang Gởi cũng)
  • Có đối tượng của bạn được lưu trữ tĩnh

vì vậy, bằng cách yêu cầu các đối số của hàm của bạn là Send, bạn cần r7'static, nhưng nó không thể sống lâu hơn s (vì nó là một tham chiếu đến con RefCell lều), không phải là 'static vì nó được xác định trong chính của bạn.

Tổng quát hơn, khi viết

fn foo<T: 'a>(bar: T); 

Bạn cần T là một trong hai:

  • Một loại với tất cả các đối số tuổi thọ của pin là 'a hoặc lâu hơn (hoặc không có đối số)
  • một &'a tham chiếu đến một loại chính nó là 'a (và bạn có thể sử dụng lại các điều kiện này)

Và như chúng ta đã thấy, T: Send ngụ ý T: 'static.

+0

Thực ra, có nhiều cách khác để đảm bảo rằng tuổi thọ đủ dài. Trong một cách tiếp cận ngã ba tham gia ví dụ, thay vì đảm bảo rằng tuổi thọ của dữ liệu là ''tĩnh' để bất kỳ tác vụ nào có thể sử dụng nó một cách an toàn, bạn đảm bảo rằng thời lượng của tác vụ không vượt quá tuổi thọ của dữ liệu. Rust không cung cấp ngã ba-gia nhập nào được nêu ra, theo như tôi biết, mặc dù. –

Các vấn đề liên quan