構造体からRustの特徴であるトレイトベースのジェネリクスまでの流れを翻訳します。
前: 5.16. Method Syntax 次: 5.20. Traits
英語できないので誤訳や改善案等、見つけた場合はご指摘の程宜しくお願いします。
- 元記事 : https://doc.rust-lang.org/stable/book/generics.html のRust1.2時点
- ライセンス : MIT license, Apache License 2.0.
- See LICENSE-APACHE, LICENSE-MIT, and COPYRIGHT for details.
ジェネリクス
度々、関数やデータ型を書いていると、引数が複数の型に対応したものが欲しくなることがあります。Rustでは、ジェネリクスを用いてそれを実現しています。ジェネリクスは型理論において'パラメトリック多相(parametric polymorphism)'と呼ばれており、与えられたパラメータにより('parametric')型や関数が多数の様相^formを持つことをそう呼びます。
さて、型理論はこれで十分です。続いてジェネリックなコードについてチェックしてみましょう。Rustが標準ライブラリで提供している型Option<T>
はジェネリックです。
enum Option<T> {
Some(T),
None,
}
<T>
の部分は、前に何度か見たことがあると思いますが、ジェネリックなデータ型であることを示しています。enumの宣言内にあるT
は同じ型に置き換えられます。型注釈を用いたOption<T>
の使用例が以下になります。
let x: Option<i32> = Some(5);
型の宣言はOption<i32>
とします。Option<T>
とどこが違って見えますか?そう、このOption
ではT
はi32
の値を持つのです。このlet束縛の右辺のSome(T)
ではT
は5
となります。i32
であれば両辺の型が一致するため、正しく型推論されます。型が不一致であれば以下のようなエラーが発生します。
let x: Option<f64> = Some(5);
// error: mismatched types: expected `core::option::Option<f64>`,
// found `core::option::Option<_>` (expected f64 but found integral variable)
f64
で特殊化したOption<T>
が作れないという意味ではありませんからね!リテラルの型も正しく合わせなければなりません。
let x: Option<i32> = Some(5);
let y: Option<f64> = Some(5.0f64);
これだけで結構です。1つ定義すれば様々な型で用いることができます。
ジェネリクスは1つの型だけに対応しているわけではありません。Rustの標準ライブラリに入っているResult<T, E>
について考えてみましょう。
enum Result<T, E> {
Ok(T),
Err(E),
}
この型ではT
とE
の_2つ_がジェネリックです。ちなみに、大文字の部分はあなたの好きにしてもらって構いません。もしあなたが望むならResult<T, E>
を、
enum Result<A, Z> {
Ok(A),
Err(Z),
}
のように定義しても良いです。"Type"から第1ジェネリックパラメータはT
とし、"Error"からE
とするのが慣例なのですが、Rustは気にしません。
Result<T, E>
は計算の結果を返すのが目的の型であり、失敗した場合にエラーを示す値を返す機能を持っています。
ジェネリック関数
私たちは似た構文でジェネリックな型をとる関数を記述することができます。
fn takes_anything<T>(x: T) {
// do something with x
}
構文は2つのパーツから成ります。<T>
は"この関数はT
というパラメータを持つジェネリック関数である"という意味であり、x: T
は"xはT
型である"という意味です。
複数の引数が同じジェネリック型を持つこともできます。
fn takes_two_of_the_same_things<T>(x: T, y: T) {
// ...
}
複数の型を取るバージョンを記述することも可能です。
fn takes_two_things<T, U>(x: T, y: U) {
// ...
}
ジェネリック関数は'トレイト束縛'と一緒に使えば最高に便利になります。これについてはトレイトの章で解説しています.
ジェネリック構造体
あなたにもジェネリック型を保存するstruct
を書くことができます。
struct Point<T> {
x: T,
y: T,
}
let int_origin = Point { x: 0, y: 0 };
let float_origin = Point { x: 0.0, y: 0.0 };
関数と同様に、<T>
でジェネリックパラメータを宣言し、型を宣言するときはx: T
とします。