我想写这个 struct :

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

struct B {
    c: &C,
}

struct C;

B.c应该从A.c借来.

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

这就是我try 的:

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并try 直接在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 comments仍然是真的:we can't store both the reference to a value and the value itself in B(我们在这里存储一个值的引用和一个值的副本).不过,这不仅仅是针对 struct ,而是所有权的运作方式.

现在,如果您不想(或不能)使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}}
    }
}

playground

Rust相关问答推荐

给定使用newype习语定义的类型上的铁 rust Vec,有没有方法获得底层原始类型的一部分?

如何在tauri—leptos应用程序中监听后端值的变化?""

值为可变对象的不可变HashMap

为什么`Vec i64`的和不知道是`Option i64`?

类型批注需要静态生存期

如何修复数组中NewType导致的运行时开销

如何强制匹配的返回类型为()?

具有多个键的 HashMap

通过写入 std::io::stdout() 输出不可见

返回迭代器考虑静态生命周期类型

当在lambda中通过引用传递时,为什么会出现终身/类型不匹配错误?

我可以在 Rust 中 serde struct camel_case 和 deserde PascalCase

有什么办法可以追踪泛型的单态化过程吗?

内部值发生变化时 Rc 的行为

为什么数组不像向量那样在 for 块之后移动?

RAII 模式的 Rust 解决方案,用于在 new() 和 drop() 上修改另一个对象

当我不满足特征界限时会发生什么?

如何在 nom 中构建负前瞻解析器?

为什么这个值在上次使用后没有下降?

如何构建包含本地依赖项的 docker 镜像?