Rust: не може да излезе от `self`, защото е заимствана грешка

Опитвам се да напиша рекурсивен метод, който добавя елемент към дърво и връща възела на дървото, съответстващ на този елемент.

enum BstNode {
    Node(int, ~BstNode, ~BstNode),
    Leaf
}

impl BstNode {
    fn insert<'a>(&'a mut self, item: int) -> &'a mut BstNode {
        match *self {
            Leaf => {
                *self = Node(item, ~Leaf, ~Leaf);
                self
            },
            Node(ref node_item, ref mut left, ref mut right) =>
                match item.cmp(node_item) {
                    Less => left.insert(item),
                    Equal => self,
                    Greater => right.insert(item)
                }
        }
    }
}

Ухапан съм от следната грешка:

bst.rs:19:30: 19:34 error: cannot move out of `self` because it is borrowed
bst.rs:19                     Equal => self,
                                       ^~~~
bst.rs:16:18: 16:31 note: borrow of `self#0` occurs here
bst.rs:16             Node(ref node_item, ref mut left, ref mut right) =>
                           ^~~~~~~~~~~~~

Какво означава „преместване от something“? Как да поправя тази грешка?

Използвам Rust 0.10.


person Krzysiek Goj    schedule 27.04.2014    source източник


Отговори (1)


Във вашия пример node_item, left и right са собственост на променливата self. Проверката на заеми не знае това в клона Equal на

match item.cmp(node_item) {
    Less => left.insert(item),
    Equal => self,
    Greater => right.insert(item)
}

нито node_item, left, нито right се използва, но вижда, че self се движи (вие го връщате), докато тези 3 променливи все още са заети (все още сте в лексикалния обхват на съвпадението, където са заети). Мисля, че това е известна грешка, че това поведение е твърде строго, вижте проблем #6993.

Що се отнася до най-добрия начин за коригиране на кода, честно казано не съм сигурен. Бих използвал напълно различна структура (поне докато не бъде коригирана предишната грешка):

pub struct BstNode {
  item: int,
  left: Option<~BstNode>,
  right: Option<~BstNode>
}

impl BstNode {
    pub fn insert<'a>(&'a mut self, item: int) -> &'a mut BstNode {
        match item.cmp(&self.item) {
            Less => match self.left {
              Some(ref mut lnode) => lnode.insert(item),
              None => {
                self.left = Some(~BstNode {item: item, left: None, right: None});
                &mut **self.left.as_mut().unwrap()
              }
            },
            Equal => self,
            Greater => match self.right {
              Some(ref mut rnode) => rnode.insert(item),
              None => {
                self.right = Some(~BstNode {item: item, left: None, right: None});
                &mut **self.right.as_mut().unwrap()
              }
            }
        }
    }
}

По този начин, когато върнете вашия възел, никога няма да имате някой от неговите членове все още зает.

person rca    schedule 28.04.2014
comment
Благодаря! Начинът да поправя моя код, като същевременно запазя структурата на данните, се оказа, че съответства на Node(node_item, _, _) (обърнете внимание на липсата на ref) и извлича ляво/надясно чрез още едно съвпадение на шаблон в клоновете Less/Greater. - person Krzysiek Goj; 29.04.2014