すべてをジェネリックにする
これまでに Option と Box を使って、ジェネリクスについて少し触れてきました。しかしこれまでは、任意の要素に対して実際にジェネリックである新しい型を宣言することは避けてきました。
実は、これは本当に簡単です。今すぐすべての型をジェネリックにしましょう。
pub struct List<T> {
head: Link<T>,
}
type Link<T> = Option<Box<Node<T>>>;
struct Node<T> {
elem: T,
next: Link<T>,
}
すべてを少し山かっこ付きにするだけで、突然コードがジェネリックになります。もちろん、これだけを行うわけにはいきません。そうしないとコンパイラがものすごく怒ります。
> cargo test
error[E0107]: wrong number of type arguments: expected 1, found 0
--> src/second.rs:14:6
|
14 | impl List {
| ^^^^ expected 1 type argument
error[E0107]: wrong number of type arguments: expected 1, found 0
--> src/second.rs:36:15
|
36 | impl Drop for List {
| ^^^^ expected 1 type argument
問題はかなり明らかです。この List というものについて話していますが、それはもはや実在しません。Option や Box と同じように、これからは常に List<Something> について話さなければなりません。
では、これらすべての impl で使う Something は何でしょうか?List と同じように、実装もすべての T で動作してほしいのです。なので List と同じように、impl も山かっこ付きにしましょう。
impl<T> List<T> {
pub fn new() -> Self {
List { head: None }
}
pub fn push(&mut self, elem: T) {
let new_node = Box::new(Node {
elem: elem,
next: self.head.take(),
});
self.head = Some(new_node);
}
pub fn pop(&mut self) -> Option<T> {
self.head.take().map(|node| {
self.head = node.next;
node.elem
})
}
}
impl<T> Drop for List<T> {
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();
}
}
}
……これで終わりです!
> 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
これで私たちのコードはすべて、T の任意の値に対して完全にジェネリックになりました。まったく、Rust は簡単ですね。特に称賛したいのは new です。これはまったく変更されていません。
pub fn new() -> Self {
List { head: None }
}
リファクタリングとコピペコーディングの守護者である Self の栄光に浸りましょう。また興味深い点として、list のインスタンスを構築するときに List<T> と書いていません。この部分は、List<T> を期待する関数からそれを返しているという事実に基づいて推論されます。
さて、まったく新しい振る舞いに進みましょう!