こんにちは。Rustに入門中のyharaです。borrow-checkerのエラーと格闘する日々ですが、今日はstd::mem::swapを使うことで解決できたことがあったのでメモしておきます。
構造体
作っているのは自作言語のパーサの部分です。今回問題になったのは以下の構造体の、locとnext_locというフィールドです。
pub struct Source<'s, 't: 's> {
...
pub loc: Location,
next_loc: Option<Location>,
...
}
locは現在位置を表します。next_locは最初はNoneですが、peek_tokenという関数を呼ぶと「次のトークンを消費した後」のLocationがセットされます。Locationはソースコード中の何行目の何文字目かを表す構造体です。
locを更新する
問題はトークンを消費したあとの処理で起こりました。next_locの値をlocに移動しようとしたのですが:
let next_loc = self.next_loc.unwrap();
self.next_loc = None;
self.loc = next_loc;
以下のエラーになりました。
error[E0507]: cannot move out of borrowed content
--> src/shiika/parser/source.rs:208:24
|
208 | let next_loc = self.next_loc.unwrap();
| ^^^^^^^^^^^^^ cannot move out of borrowed content
letで一時変数を定義する行為はmoveになるので、moveしたあとのself.next_locはどうしたらいいの?と怒られている…ような気がします。
swap
で、「rust swap」で検索したところ、以下の記事が見つかりました。
これによるとuse std::memのstd::mem::swapを使うと、2つのlocationの値だけを入れ替えることができるようです。今回はself.next_locをNoneにしつつ元の値も取りたいので、こんな感じですね。
let mut tmp = None;
std::mem::swap(&mut tmp, &mut self.next_loc);
まとめ
ということで以下のようにすることでコンパイルが通るようになりました。1
// Set next_loc to self.loc (using swap for coaxing the borrow-checker)
let mut tmp = None;
std::mem::swap(&mut tmp, &mut self.next_loc);
self.loc = tmp.unwrap();
Rustは最初は何もわからん状態でしたが、徐々に勘が働くようになってきた気がします。C++に慣れてる人なら理解が早いと思います(私は文法をいくらか知ってる程度だったので苦労していますが)。
-
コメントのcoaxは「なだめる」のつもりで使っていますが辞書引いて出てきただけなので適切かはわからん ↩