簡體   English   中英

在 Rust 中使用帶有結構的生命周期的正確方法是什么?

[英]What is the correct way to use lifetimes with a struct in Rust?

我想寫這個結構:

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

struct B {
    c: &C,
}

struct C;

Bc應該是從Ac借來的。

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

這就是我嘗試過的: struct C;

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

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

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

fn main() {}

但它失敗了:

error[E0597]: `c` does not live long enough
  --> src/main.rs:17:24
   |
17 |             b: B { c: &c },
   |                        ^ borrowed value does not live long enough
18 |         }
19 |     }
   |     - borrowed value only lives until here
   |
note: borrowed value must be valid for the lifetime 'b as defined on the method body at 13:5...
  --> src/main.rs:13:5
   |
13 |     fn new<'b>() -> A<'b> {
   |     ^^^^^^^^^^^^^^^^^^^^^

error[E0382]: use of moved value: `c`
  --> src/main.rs:17:24
   |
16 |             c: c,
   |                - value moved here
17 |             b: B { c: &c },
   |                        ^ value used here after move
   |
   = note: move occurs because `c` has type `C`, which does not implement the `Copy` trait

我已經閱讀了關於所有權的 Rust 文檔,但我仍然不知道如何修復它。

實際上,上面的代碼失敗的原因不止一個。 讓我們把它分解一下,並探討一些關於如何修復它的選項。

首先讓我們刪除new並嘗試直接在main中構建A的實例,以便您看到問題的第一部分與生命周期無關:

struct C;

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

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

fn main() {
    // I copied your new directly here
    // and renamed c1 so we know what "c"
    // the errors refer to
    let c1 = C;

    let _ = A {
        c: c1,
        b: B { c: &c1 },
    };
}

這失敗了:

error[E0382]: use of moved value: `c1`
  --> src/main.rs:20:20
   |
19 |         c: c1,
   |            -- value moved here
20 |         b: B { c: &c1 },
   |                    ^^ value used here after move
   |
   = note: move occurs because `c1` has type `C`, which does not implement the `Copy` trait

它所說的是,如果您將c1分配給c ,您將其所有權移至c (即您不能再通過c1訪問它,只能通過c )。 這意味着對c1的所有引用都將不再有效。 但是你有一個&c1仍然在范圍內(在 B 中),所以編譯器不能讓你編譯這段代碼。

當編譯器說C類型不可復制時,編譯器會在錯誤消息中提示可能的解決方案。 如果您可以制作C的副本,那么您的代碼將是有效的,因為將c1分配給c將創建該值的新副本,而不是移動原始副本的所有權。

我們可以通過改變它的定義來使C可復制,如下所示:

#[derive(Copy, Clone)]
struct C;

現在上面的代碼可以工作了。 請注意, @matthieu-m 的評論仍然是正確的:我們不能同時在 B 中存儲對值的引用和值本身(我們在此處存儲對值的引用和值的副本)。 然而,這不僅適用於結構,它也是所有權的工作方式。

現在,如果您不想(或不能)使C可復制,則可以將引用存儲在AB中。

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

那么一切都好嗎? 不是真的......我們仍然想將A的創建移回new方法。 這就是我們一生都會遇到麻煩的地方。 讓我們將A的創建移回方法中:

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

正如預期的那樣,這是我們一生的錯誤:

error[E0597]: `c1` does not live long enough
  --> src/main.rs:17:17
   |
17 |             c: &c1,
   |                 ^^ borrowed value does not live long enough
...
20 |     }
   |     - borrowed value only lives until here
   |
note: borrowed value must be valid for the lifetime 'a as defined on the impl at 13:1...
  --> src/main.rs:13:1
   |
13 | impl<'a> A<'a> {
   | ^^^^^^^^^^^^^^

error[E0597]: `c1` does not live long enough
  --> src/main.rs:18:24
   |
18 |             b: B { c: &c1 },
   |                        ^^ borrowed value does not live long enough
19 |         }
20 |     }
   |     - borrowed value only lives until here
   |
note: borrowed value must be valid for the lifetime 'a as defined on the impl at 13:1...
  --> src/main.rs:13:1
   |
13 | impl<'a> A<'a> {
   | ^^^^^^^^^^^^^^

這是因為c1new方法結束時被銷毀,所以我們不能返回對它的引用。

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

一種可能的解決方案是在new之外創建C並將其作為參數傳入:

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

操場

在#rust IRC 上檢查 Manishearth 和 eddyb 后,我相信結構不可能存儲對自身或自身一部分的引用。 所以你想要做的事情在 Rust 的類型系統中是不可能的。

派對遲到了(來自未來的回復)並且對 Rust 完全陌生,但我到了那里(有點)。 建立在對我有用的答案的基礎上,至少編譯明智。

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

}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM