Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

すべてをジェネリックにする

これまでに 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> を期待する関数からそれを返しているという事実に基づいて推論されます。

さて、まったく新しい振る舞いに進みましょう!