Как правильно управлять владением с заимствованиями в Rust? - PullRequest
2 голосов
/ 02 мая 2020

Я довольно новичок ie в мире Rust и до сих пор не до конца понимаю, как работает владение / заимствование / пожизненное. У меня есть этот пример, чтобы продемонстрировать борьбу:

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

Как правильно передать право собственности (как в примере выше) без копирования, но с заимствованием, переходя по следующей ссылке (без добавления дополнительных параметров)? Мне все еще не ясно с этой частью ...

Ответы [ 2 ]

2 голосов
/ 02 мая 2020
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, однако Вы хотите вернуть a Node. Возникает вопрос: хотите ли вы вернуть другой (то есть new Node) или вы хотите извлечь (то есть take) Node из next и вернуть его. В случае, если вы хотите 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)
}
2 голосов
/ 02 мая 2020

Если вы хотите, чтобы 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()
}

Также прочитайте

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...