Rust のギモン その 1

Rust を使ってまだ日が浅いということもあって、ある処理を Rust でどう書くのが普通なのかよくわからないことが多々ある。ここに記しておけば誰か親切な人が教えてくれるかもしれない。

なお、「その 1」とあるけれど、「その 2」以降があるかは未定。

疑問

Linked List を考える。ここでは簡単のため、以下のように Node を定義する。

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

この Node を連ねた LinkedList を用意する。

struct LinkedList {
    head: Option<Box<Node>>,
}

LinkedList に新しく Node を追加する処理を書きたい。その際、追加する value が既に含まれている場合には何もせず、value が存在しなかった時のみ最後尾に Node を追加する。

そのため、先頭から Node を順に見て、value が見つかったらその Node への reference を、見つからなかったら新しい Node の挿入場所を返す LinkedList のメソッド locate_mut() を作る。

    fn locate_mut(&mut self, value: i32) -> &mut Option<Box<Node>> {
        let mut n = &mut self.head;

        loop {
            match { n } {
                Some(ref mut node) if node.value != value => n = &mut node.next,
                other @ _ => return other,
            }
        }
    }

そして、この locate_mut() を利用して実際の挿入を行う add_node() を実装する。

    fn add_node(&mut self, value: i32) {
        match self.locate_mut(value) {
            Some(_) => {}
            p @ None => {
                *p = Some(Box::new(Node::new(value)));
            }
        }
    }

で、これをコンパイルすると怒られる。

warning[E0505]: cannot move out of `_` because it is borrowed
  --> ./hoge.rs:31:17
   |
25 |     fn locate_mut(&mut self, value: i32) -> &mut Option<Box<Node>> {
   |                   - let's call the lifetime of this reference `'1`
...
30 |                 Some(ref mut node) if node.value != value => n = &mut node.
   |                      ------------ borrow of value occurs here
31 |                 other @ _ => return other,
   |                 ^^^^^^^^^           ----- returning this value requires tha
   |                 |
   |                 move out of value occurs here
   |
   = warning: this error has been downgraded to a warning for backwards compatib
   = warning: this represents potential undefined behavior in your code and this

warning のメッセージを読む限りでは Some(ref mut node)node として borrow した値を other @ _ => return other,other として move out しているのが問題っぽい。

Some の arm で borrow すると _ の arm に入っても borrow しっぱなしなのかとも考えたのだけれど、どうも違うよう。というのも、同一の処理で Some(ref node) だと怒られないから。mut が付くと問題になるみたい。
また、guard の if node.value != value がなくても warning は出ない。

mut で guard がある時に問題になるということは guard の中で値を変えられる可能性があるのが原因なのだろうか。だとするとちょっと warning のメッセージからは読み取れない。

もちろん他の方法で実装すれば良いし、何だかんだいってそれが一番楽な解決法だというものわかる。ただ、素直に実装しただけのつもりなのに良く分からない怒られ方をしたのが不思議で、何故なのかを知っておきたい。

f:id:mtXTJocj:20190728233456p:plain