繁体   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