2014-12-21 17 views
8

Tôi muốn viết cấu trúc này:Làm thế nào để viết đúng cấu trúc tuổi thọ trong Rust?

struct A { 
    b: B, 
    c: C, 
} 

struct B { 
    c: &C, 
} 

struct C; 

Các B.c nên được vay mượn từ A.c.

A -> 
    b: B -> 
    c: &C -- borrow from --+ 
          | 
    c: C <------------------+ 

Đây là những gì tôi đã cố gắng:

struct C; 

struct B<'b> { 
    c: &'b C, 
} 


struct A<'a> { 
    b: B<'a>, 
    c: C 
} 

impl<'a> A<'a> { 
    fn new<'a>() -> A<'a> { 
     let c = C; 
     A {c: c, b: B{c: &c}} 
    } 
} 

Nhưng nó không thành công:

test.rs:17:21: 17:22 error: `c` does not live long enough 
test.rs:17   A {b: B{c: &c}, c: &c} 
          ^
test.rs:15:27: 18:6 note: reference must be valid for the lifetime 'a as defined on the block at 15:26... 
test.rs:15  fn new<'a>() -> A<'a> { 
test.rs:16   let c = C; 
test.rs:17   A {b: B{c: &c}, c: &c} 
test.rs:18  } 
test.rs:15:27: 18:6 note: ...but borrowed value is only valid for the block at 15:26 
test.rs:15  fn new<'a>() -> A<'a> { 
test.rs:16   let c = C; 
test.rs:17   A {b: B{c: &c}, c: &c} 
test.rs:18  } 
test.rs:17:29: 17:30 error: `c` does not live long enough 
test.rs:17   A {b: B{c: &c}, c: &c} 
            ^
test.rs:15:27: 18:6 note: reference must be valid for the lifetime 'a as defined on the block at 15:26... 
test.rs:15  fn new<'a>() -> A<'a> { 
test.rs:16   let c = C; 
test.rs:17   A {b: B{c: &c}, c: &c} 
test.rs:18  } 
test.rs:15:27: 18:6 note: ...but borrowed value is only valid for the block at 15:26 
test.rs:15  fn new<'a>() -> A<'a> { 
test.rs:16   let c = C; 
test.rs:17   A {b: B{c: &c}, c: &c} 
test.rs:18  } 
error: aborting due to 2 previous errors 

Tôi đã đọc các tài liệu hướng dẫn Rust trên quyền sở hữu, nhưng tôi vẫn không biết làm thế nào để sửa nó.

Nếu có bất kỳ bài viết hữu ích nào, hãy cho tôi biết trong phần nhận xét.

+0

"đó là lỗi" không phải là một lời giải thích đầy đủ về vấn đề này. Nếu trình biên dịch đưa ra một lỗi, vui lòng gửi nó và cho chúng tôi biết dòng đó là gì. –

+0

@DavidGrayson cảm ơn, tôi đã đăng. –

+0

@geotheory Xin lỗi, tiếng Anh của tôi rất tệ, bây giờ tôi đã thay đổi tiêu đề. –

Trả lời

24

Có nhiều lý do tại sao mã ở trên không thành công. Hãy chia nhỏ một chút và khám phá một vài tùy chọn về cách khắc phục. Đầu tiên chúng ta hãy loại bỏ các new và cố gắng xây dựng một thể hiện của A trực tiếp trong chính, vì vậy mà bạn thấy rằng phần đầu tiên của vấn đề không có gì để làm với cuộc đời:

struct C; 

struct B<'b> { 
    c: &'b C, 
} 

struct A<'a> { 
    b: B<'a>, 
    c: C 
} 

fn main() { 
    // I copied your new directly here 
    let c1 = C; // and renamed c1 so we know what "c" 
       // the errors refer to 
    let _ = A {c: c1, b: B{c: &c1}}; 
} 

này không thành công với:

error: use of moved value: `c1` 
let _ = A {c: c1, b: B{c: &c1}}; 
         ^
note: `c1` moved here because it has type `C`, which is non-copyable 
let _ = A {c: c1, b: B{c: &c1}}; 
      ^

những gì nó nói là nếu bạn chỉ định c1, c, bạn chuyển quyền sở hữu của nó sang c (nghĩa là bạn không thể truy cập nó nữa qua c1, chỉ thông qua c). Điều này có nghĩa là tất cả các tham chiếu đến c1 sẽ không còn giá trị. Nhưng bạn có một &c1 vẫn còn trong phạm vi (trong B), vì vậy rustc không thể cho phép bạn biên dịch mã này.

Trình biên dịch gợi ý một giải pháp có thể có trong thông báo lỗi khi thông báo lỗi loại C không thể sao chép được. Nếu bạn có thể tạo một bản sao của C, mã của bạn sau đó sẽ hợp lệ, bởi vì gán c1 cho c sẽ tạo một bản sao mới của giá trị thay vì di chuyển quyền sở hữu của bản gốc.

Chúng ta có thể thử điều này và làm cho C copyable bằng cách thay đổi định nghĩa của nó như thế này:

#[derive(Copy)] 
struct C; 

Bây giờ mã trên công trình. Lưu ý rằng những gì @ matthieu-m nói trong bình luận của mình vẫn đúng. Chúng tôi không thể lưu trữ cả tham chiếu đến giá trị và giá trị trong B (chúng tôi đang lưu trữ tham chiếu đến giá trị và COPY của giá trị tại đây). Tuy nhiên, đó không chỉ là cấu trúc, đó là cách hoạt động của quyền sở hữu.

Bây giờ, nếu bạn không muốn (hoặc không thể) làm cho C có thể sao chép được, bạn có thể lưu trữ các tham chiếu ở cả A và B thay thế.

struct C; 

struct B<'b> { 
    c: &'b C, 
} 

struct A<'a> { 
    b: B<'a>, 
    c: &'a C // now this is a reference too 
} 

fn main() { 
    let c1 = C; 
    let _ = A {c: &c1, b: B{c: &c1}}; 
} 

Tất cả tốt sau đó? Không thực sự ... chúng tôi vẫn muốn di chuyển việc tạo ra A trở lại phương thức new. Và đó là nơi chúng ta sẽ gặp rắc rối với kiếp sống. Hãy di chuyển việc tạo A trở lại vào một impl.

impl<'a> A<'a> { 
    fn new() -> A<'a> { 
     let c1 = C; 
     A {c: &c1, b: B{c: &c1}} 
    } 
} 

như dự đoán, đây là lỗi cuộc đời của chúng tôi:

error: `c1` does not live long enough 
A {c: &c1, b: B{c: &c1}} 
     ^~ 

này là bởi vì c1 bị phá hủy vào cuối của phương pháp new, vì vậy chúng tôi không thể trả về một tham chiếu đến nó.

{   
    let c1 = C; // we create c1 here 
    A {c: &c1, b: B{c: &c1}} // ...take a reference to it 
} // and destroy c1 here (so we can't return A with a reference to c1) 

Một giải pháp có thể có là tạo C bên ngoài new và chuyển nó làm tham số. Toàn bộ mã ở đây:

struct C; 

struct B<'b> { 
    c: &'b C, 
} 

struct A<'a> { 
    b: B<'a>, 
    c: &'a C 
} 

fn main() { 
    let c1 = C; 
    let _ = A::new(&c1); 
} 

impl<'a> A<'a> { 
    fn new(c: &'a C) -> A<'a> { 
     A {c: c, b: B{c: c}} 
    } 
} 

playpen

3

Sau khi kiểm tra với Manishearth và eddyb trên #rust IRC, tôi tin rằng không thể cấu trúc để lưu trữ tham chiếu đến chính nó hoặc một phần của chính nó. Vì vậy, những gì bạn đang cố gắng làm là không thể trong hệ thống kiểu của Rust.

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