基本
さて、ここは本書の中でもひどい部分で、この章を書くのに 7 年もかかった理由です! すでに 5 回はやった本当に退屈なことを大量に一気に片付ける時間です。ただし、すべてを 2 回ずつ、しかも Option<NonNull<Node<T>>> でやらなければならないので、やたら冗長で長くなります!
impl<T> LinkedList<T> {
pub fn new() -> Self {
Self {
front: None,
back: None,
len: 0,
_boo: PhantomData,
}
}
}
PhantomData はフィールドを持たない奇妙な型なので、型名を言うだけで作れます。肩すくめ
pub fn push_front(&mut self, elem: T) {
// 安全性: リンクリストなんだから、何を求めてる?
unsafe {
let new = NonNull::new_unchecked(Box::into_raw(Box::new(Node {
front: None,
back: None,
elem,
})));
if let Some(old) = self.front {
// 新しい先頭を古いものの前に置く
(*old).front = Some(new);
(*new).back = Some(old);
} else {
// 先頭がないなら、これは空リストなので back も
// 設定する必要がある。ついでに、やらかした場合に備えて
// テスト用の整合性チェックも入れておく。
debug_assert!(self.back.is_none());
debug_assert!(self.front.is_none());
debug_assert!(self.len == 0);
self.back = Some(new);
}
self.front = Some(new);
self.len += 1;
}
}
error[E0614]: type `NonNull<Node<T>>` cannot be dereferenced
--> src\lib.rs:39:17
|
39 | (*old).front = Some(new);
| ^^^^^^
ああそうだ、私は本当にこのポインターっぽい子供たちが大嫌いです。as_ptr で NonNull から生ポインターを明示的に取り出す必要があります。なぜなら DerefMut は &mut を使って定義されていて、unsafe コードの中に安全な参照を無造作に持ち込みたくないからです!
(*old.as_ptr()).front = Some(new);
(*new.as_ptr()).back = Some(old);
Compiling linked-list v0.0.3
warning: field is never read: `elem`
--> src\lib.rs:16:5
|
16 | elem: T,
| ^^^^^^^
|
= note: `#[warn(dead_code)]` on by default
warning: `linked-list` (lib) generated 1 warning (1 duplicate)
warning: `linked-list` (lib test) generated 1 warning
Finished test [unoptimized + debuginfo] target(s) in 0.33s
いいですね。次は pop(と len)です。
pub fn pop_front(&mut self) -> Option<T> {
unsafe {
// pop する先頭ノードがある場合にだけ、何かをする必要がある。
// もはや `take` をいじくり回す必要がないことに注意。
// なぜなら、すべてが Copy であり、失敗しても実行される
// デストラクタはないから……そうだよね? :) そーうだよね? :)))
self.front.map(|node| {
// Box を生き返らせて、その値を move し、
// Drop できるようにする(Box は引き続きこれを魔法のように理解してくれる)。
let boxed_node = Box::from_raw(node.as_ptr());
let result = boxed_node.elem;
// 次のノードを新しい先頭にする。
self.front = boxed_node.back;
if let Some(new) = self.front {
// 削除されたノードへの参照を片付ける
(*new.as_ptr()).front = None;
} else {
// 先頭が null になったなら、このリストは空になった!
debug_assert!(self.len == 1);
self.back = None;
}
self.len -= 1;
result
// ここで Box は暗黙的に解放され、T がないことを認識している。
})
}
}
pub fn len(&self) -> usize {
self.len
}
Compiling linked-list v0.0.3
Finished dev [unoptimized + debuginfo] target(s) in 0.37s
私には妥当に見えます。テストを書く時間です!
#[cfg(test)]
mod test {
use super::LinkedList;
#[test]
fn test_basic_front() {
let mut list = LinkedList::new();
// 空リストを壊そうとしてみる
assert_eq!(list.len(), 0);
assert_eq!(list.pop_front(), None);
assert_eq!(list.len(), 0);
// 要素が 1 つのリストを壊そうとしてみる
list.push_front(10);
assert_eq!(list.len(), 1);
assert_eq!(list.pop_front(), Some(10));
assert_eq!(list.len(), 0);
assert_eq!(list.pop_front(), None);
assert_eq!(list.len(), 0);
// いじくり回す
list.push_front(10);
assert_eq!(list.len(), 1);
list.push_front(20);
assert_eq!(list.len(), 2);
list.push_front(30);
assert_eq!(list.len(), 3);
assert_eq!(list.pop_front(), Some(30));
assert_eq!(list.len(), 2);
list.push_front(40);
assert_eq!(list.len(), 3);
assert_eq!(list.pop_front(), Some(40));
assert_eq!(list.len(), 2);
assert_eq!(list.pop_front(), Some(20));
assert_eq!(list.len(), 1);
assert_eq!(list.pop_front(), Some(10));
assert_eq!(list.len(), 0);
assert_eq!(list.pop_front(), None);
assert_eq!(list.len(), 0);
assert_eq!(list.pop_front(), None);
assert_eq!(list.len(), 0);
}
}
Compiling linked-list v0.0.3
Finished test [unoptimized + debuginfo] target(s) in 0.40s
Running unittests src\lib.rs
running 1 test
test test::test_basic_front ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
やったー、私たちは完璧です!
……そうですよね?