如何在Rust中正确管理所有权与借款?

问题描述 投票:2回答:1

我在Rust世界里是个新手,还没有完全理解所有权借贷lifetime的工作原理。 我有这个例子来证明一个挣扎。

struct Node {
  value: bool,
  next: Option<Box<Node>>
}

fn populate(next: &mut Option<Box<Node>>) -> Option<Node> {
  let node = Node { value: true, next: None };
  let result = Some(Box::new(node));
  *next = result;
  Some(*next.unwrap())
}

fn main() {
  let mut node = Node {
    value: false,
    next: None
  };
  let result = populate(&mut node.next);
  println!("{}", node.unwrap().value);
  println!("{}", result.unwrap().value);
}

我不明白为什么移动这种方式可以用。

fn populate(next: &mut Option<Box<Node>>) -> Option<Node> {
  let node = Node { value: true, next: None };
  let result = Some(Box::new(node));
  // *next = result;
  Some(*result.unwrap() /* *next.unwrap() */)
}

但另一种方式却不行

fn populate(next: &mut Option<Box<Node>>) -> Option<Node> {
      let node = Node { value: true, next: None };
      let result = Some(Box::new(node));
      *next = result;
      Some(*(*next.as_ref().unwrap())) // or Some(*next.unwrap())
 }

如何正确地转移所有权(就像上面的例子一样),不需要复制,而是借用突变的下一个引用(并且不添加额外的参数)?我还是不清楚这部分......

rust nodes lifetime ownership borrowing
1个回答
2
投票

如果你想 populate 返回对新的 Node 内置于 next,引用需要成为返回类型的一部分。你不能将节点的所有权转移到 next 的同时也要返回它,这不是所有权的工作方式。

fn populate(next: &mut Option<Box<Node>>) -> Option<&mut Node> {
//                                            here: ^^^^

你可以尝试返回 Some(&mut *next.unwrap())但这是行不通的,因为 unwrap 需要 self 的值。幸运的是,有一个方便的功能,在 Option 会带你直接从 &mut Option<Box<Node>>Option<&mut Node>, as_deref_mut:

fn populate(next: &mut Option<Box<Node>>) -> Option<&mut Node> {
    let node = Node {
        value: true,
        next: None,
    };
    *next = Some(Box::new(node));
    next.as_deref_mut()
}

另见


2
投票
fn populate(next: &mut Option<Box<Node>>) -> Option<Node> {
  let node = Node { value: true, next: None };
  let result = Some(Box::new(node));
  *next = result;
  Some(*result.unwrap() /* *next.unwrap() */)
}

按照编译器的建议按摩代码,可能会导致你写的东西。现在,拿着它,引入中间变量并注释类型(看看是怎么回事),就会得到这样的结果。

fn populate2(next: &mut Option<Box<Node>>) -> Option<Node> {
    let node : Node = Node { value: true, next: None };
    let result : Option<Box<Node>> = Some(Box::new(node));
    *next = result;
    let next_as_ref : Option<&Box<Node>> = next.as_ref();
    let next_as_ref_unwrap : &Box<Node> = next_as_ref.unwrap(); 
    let next_as_ref_unwrap_deref : Box<Node> = *next_as_ref_unwrap; // <- error here!
    Some(*next_as_ref_unwrap_deref) // or Some(*next.unwrap())
}

let next_as_ref_unwrap_deref : Box<Node> = *next_as_ref_unwrap; 失败,因为 next_as_ref_unwrap 是借来的 Box<Node>即a &Box<Node>. Dereferencing(即 *) next_as_ref_unwrap 试图移动,这不能从借来的变量中完成。

问题是,你有 next,其中包含(基本上)一个 Node然而,你想 返回 a Node. 这就提出了一个问题。你是否要返回另一个(即 新的 Node),还是要提取(即) take)的 Nodenext 并将其退回。如果你想 take 并将其返回。

fn populate(next: &mut Option<Box<Node>>) -> Option<Node> {
  let node = Node { value: true, next: None };
  let result = Some(Box::new(node));
  *next = result;
  next.take().map(|boxed_node| *boxed_node)
}

上面的代码可以编译,但至少是可疑的,因为它接受了一个... next 本质上是作为一个局部变量使用的,并使之成为 None 之后(因为我们 take 从它)。)

你可能要决定什么是 populate 其实应该做的。

  • 是否应该 修改 None? 为什么返回值 Option<None>? 是否应该返回 next的旧值?(为什么要返回 Option<Node> 而不是 Option<Box<Node>> 然后呢?)

码。

fn populate_returning_old_val(next: &mut Option<Box<Node>>) -> Option<Node> {
  std::mem::replace(
    next,
    Some(Box::new(Node { value: true, next: None }))
  ).take().map(|boxed_node| *boxed_node)
}
© www.soinside.com 2019 - 2024. All rights reserved.