如何通过借用 Rust 来正确管理所有权?

How to manage properly ownership with borrowing in Rust?

我是 Rust 世界的新手,仍然不完全理解 ownership/borrowing/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())
 }

如何在不复制但借用变异下一个引用(并且不添加额外参数)的情况下正确转移所有权(如上例所示)?这部分我还不是很清楚...

如果你想 populate 到 return 对新 Node 的引用放在 next 里面,引用需要是 return 的一部分类型。您不能将节点移动(转移所有权)到 next 中,同时还 returning 它;这不是所有权的运作方式:

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

您可能会尝试 return 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()
}

另读

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>,即 &Box<Node>。取消引用(即 *next_as_ref_unwrap 尝试移动,这不能从借用的变量中完成。

问题是您有 next,其中(基本上)包含一个 Node,但是,您想要 return Node。这就提出了一个问题:你想 return 另一个(即 new Node),还是你想提取(即 takeNode 来自 next 和 return 它。如果你想 take 和 return 它:

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 ,这个 next 本质上用作局部变量并在之后生成 None (因为我们 take

您可能想决定 populate 实际上应该做什么。

  • 是否应该修改 None?为什么 return 值 Option<None>?它应该是 return next 的旧值吗? (为什么 return 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)
}