Optionを使う
特別目ざとい人は私達が劣化Optionを再発明したことに気づいたかもしれません:
enum Link {
Empty,
More(Box<Node>),
}
LinkはOption<Box<Node>>
と同じことです.かといってOption<Box<Node>>
を
どこにでも書くのは気が引けますし,pop
の戻り値とは違いLinkは外部から
見ることはできないので,これをLinkと呼び続けることは問題なさそうです.
しかし,Optionには超いいメソッドがいくつもあり,Optionを使えば
(私達がやったように)それらを自力で実装することもありません.なので
全部Optionを使いましょう.まずは愚直に全部のMoreとEmptyをSomeとNoneに
書き換えていきましょう:
use std::mem;
pub struct List {
head: Link,
}
// 型エイリアスです!イエーイ!
type Link = Option<Box<Node>>;
struct Node {
elem: i32,
next: Link,
}
impl List {
pub fn new() -> Self {
List { head: None }
}
pub fn push(&mut self, elem: i32) {
let new_node = Box::new(Node {
elem: elem,
next: mem::replace(&mut self.head, None),
});
self.head = Some(new_node);
}
pub fn pop(&mut self) -> Option<i32> {
match mem::replace(&mut self.head, None) {
None => None,
Some(node) => {
self.head = node.next;
Some(node.elem)
}
}
}
}
impl Drop for List {
fn drop(&mut self) {
let mut cur_link = mem::replace(&mut self.head, None);
while let Some(mut boxed_node) = cur_link {
cur_link = mem::replace(&mut boxed_node.next, None);
}
}
}
これは若干良くなった程度ですが,さらなる良さがOptionのメソッドを使うことで 得られます.
まずmem::replace(&mut option, None)
はめっちゃよくある形で,
あまりにもよくあるのでOptionにはそのためのtake
というメソッドがあります.
pub struct List {
head: Link,
}
type Link = Option<Box<Node>>;
struct Node {
elem: i32,
next: Link,
}
impl List {
pub fn new() -> Self {
List { head: None }
}
pub fn push(&mut self, elem: i32) {
let new_node = Box::new(Node {
elem: elem,
next: self.head.take(),
});
self.head = Some(new_node);
}
pub fn pop(&mut self) -> Option<i32> {
match self.head.take() {
None => None,
Some(node) => {
self.head = node.next;
Some(node.elem)
}
}
}
}
impl Drop for List {
fn drop(&mut self) {
let mut cur_link = self.head.take();
while let Some(mut boxed_node) = cur_link {
cur_link = boxed_node.next.take();
}
}
}
次にmatch option { None => None, Some(x) => Some(y) }
もめっちゃよくある形で,
あまりにもよくあるのでmap
というメソッドがあります.map
にはSome(x)
のx
からSome(y)
のy
を作る関数を渡す必要があります.fn
で関数を宣言して渡す
のもいいですが,インラインで書くほうがいいでしょう.
そのためにはクロージャを使います.クロージャは,クロージャ外のローカル変数を
参照することができる無名関数です.この強力な機能によって,条件に依存する処理を
超簡単に書くことができます.match
を使っているのはpop
だけですね.書き換えて
みましょう:
pub fn pop(&mut self) -> Option<i32> {
self.head.take().map(|node| {
self.head = node.next;
node.elem
})
}
アーイイ.今回の変更で何もぶっ壊れてないことを確認しましょう:
> cargo test
Running target/debug/lists-5c71138492ad4b4a
running 2 tests
test first::test::basics ... ok
test second::test::basics ... ok
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured
いいですね!次はコードの挙動を改善していきましょう!