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

第21章 ジェネリクスとトレイト

この章の目的

この章の終わりまでに、トレイトで型に振る舞いを付与し、ジェネリクスで多くの型に対して機能する関数を書けるようになります。

トレイト、すなわち名前の付いた役割

アンサンブルにおいて、「旋律を担う楽器」のような役割は、ある晩はフルートによって、次の晩はヴァイオリンによって満たされます。役割には名前が付いており、それを満たす楽器は変わります。トレイトとは、型に対する名前の付いた役割です。

トレイトは振る舞いを宣言します。impl ブロックは、その振る舞いを1つの特定の型に対して提供します。

trait Transpose {
    fn up_octave(x: Word) -> Word;
}

impl Transpose for Word {
    fn up_octave(x: Word) -> Word {
        x + 12
    }
}

fn main() -> Word {
    let n: Word = 60;
    n.up_octave()
}

keleusma run で実行してください。出力は 72 です。

trait Transpose は、この役割を満たす型が up_octave の振る舞いを持つことを宣言します。impl Transpose for Word はそれを提供します。Word にとって、1オクターブ上げることは12を加えることです。呼び出し n.up_octave() はそれを使います。ドットの前の値 n が、その振る舞いが作用する対象です。

n.up_octave().up_octave() のように、あるメソッドを呼び出してその結果に別のメソッドを呼び出すには、当面の間、その間に型付きの束縛が必要です。中間結果を let m: Word = n.up_octave(); で束縛し、次のメソッドを m に対して呼び出してください。

ジェネリクス、すなわち多くの型のための関数

ジェネリック関数は一度書かれ、多くの型に対して機能します。それが作用する型はパラメータとして、山括弧の中の代役の名前として残されます。

fn first<T>(a: T, b: T) -> T {
    a
}

fn main() -> Word {
    first(64, 67)
}

実行してください。出力は 64 です。

<T>T という名前の型パラメータを導入します。first の内側では、両方のパラメータと結果が T であり、T が何であれそうなります。呼び出し first(64, 67)Word 値を使うため、その呼び出しでは TWord です。同じ関数は Float 値や他のどんな型にも役立ちます。ジェネリック関数とは、どんな楽器であっても機能するように書かれたフレーズです。

const ジェネリクス、すなわちコンパイル時の数

型パラメータは型の代役を務めます。const パラメータは、コンパイル時に固定された数の代役を務めます。それは山括弧の中に const n: Word と書かれ、本体の内側では n は通常の Word 値です。

fn plus<const n: Word>() -> Word {
    n + 10
}

fn main() -> Word {
    plus::<7>()
}

実行してください。出力は 17 です。名前の後の ::<7> はターボフィッシュであり、この呼び出しに const 値を供給します。const 値は常にこのように書き出され、決して推論されません。なぜなら、コンパイラがそれを読み取るための値引数が存在しないからです。

const パラメータは配列の長さを設定できるため、関数はサイズがそのシグネチャの一部である固定サイズのバッファを受け取ることができます。

fn first<const n: Word>(a: [Word; n]) -> Word {
    a[0]
}

fn main() -> Word {
    first::<3>([10, 20, 30])
}

出力は 10 です。構造体も const パラメータを取り、任意の型パラメータの後に混ぜて置かれ、構築時には同じターボフィッシュで const を供給します。

struct Buf<const n: Word> {
    items: [Word; n],
}

fn get(b: Buf<3>) -> Word {
    b.items[2]
}

fn main() -> Word {
    get(Buf::<3> { items: [10, 20, 30] })
}

出力は 30 です。const 値は、Buf<n + 1>Multiword<2 * n> のように、+-* を使って他の const 値から構築できます。const の除算は存在しないため、const 算術は常に全域です。

すべての const パラメータは、プログラムが特殊化されるとき、最悪ケースの境界が計算される前に、その具体的な数に置き換えられます。したがって検証器は決して記号的なサイズを見ることがありません。[Word; n] は、そのメモリが測定される時点までには [Word; 3] になっています。これが、const ジェネリクスが確定的な時間およびメモリの境界を保つ理由です。

この章のまとめ

  • trait は名前の付いた振る舞いを宣言し、impl ブロックはその振る舞いを1つの型に対して提供します。
  • value.method() は、ドットの前の値に作用して振る舞いを呼び出します。
  • ジェネリック関数は、<T> と書かれる型パラメータを使い、一度に多くの型に対して機能します。
  • <const n: Word> と書かれる const パラメータは、コンパイル時の数の代役を務め、ターボフィッシュ f::<7>() によって供給され、配列の長さ、Multiword の次元、または Word 値として使用できます。それは境界が計算される前に、その具体的な数に消去されます。

次の章では、型に独自の名前と規則を与えます。