Rustを勉強していてジェネリクスの書き方が覚えられなかったので、備忘として記事に残しておこうと思います。
ジェネリクスとは?
ジェネリクスを一言でいうと、
同じコードを、いろんな型に対応できるようにする仕組み
また、ジェネリクスは慣習的にT
やU
が使われる
ジェネリクス付き構造体
固定型の構造体
struct Point {
x: i32,
y: i32,
}
Code language: CSS (css)
これはi32
専用の Point
。
ジェネリクス付き
struct Point<T> {
x: T,
y: T,
}
fn main() {
let int_point = Point { x: 5, y: 10 }; // i32
let float_point = Point { x: 1.2, y: 3.4 }; // f64
}
Code language: JavaScript (javascript)
この場合<T>
はi32
やf64
などとして扱うことができる
ジェネリクス付き関数
fn pair<T>(a: T, b: T) -> (T, T) {
(a, b)
}
fn main() {
let int_pair = pair(1, 2); // (i32, i32)
let str_pair = pair("a", "b"); // (&str, &str)
}
Code language: JavaScript (javascript)
関数の場合はfn pair<T>
のように関数名の後にジェネリック型を利用することを宣言する
複数のジェネリクス
struct Pair<T, U> {
first: T,
second: U,
}
fn main() {
let p = Pair { first: 1, second: 11.0 };
}
Code language: HTML, XML (xml)
T
とU
は別々の型Pair<i32, f64>
などとして使われる
impl とジェネリクス
「構造体にメソッドを生やすとき」に出てくるのが impl
ジェネリクスを使った構造体の場合、impl
側でもジェネリクスを宣言する必要がある
struct Point<T> {
x: T,
y: T,
}
// impl にも <T> が必要!
impl<T> Point<T> {
fn new(x: T, y: T) -> Self {
Self { x, y }
}
fn x(&self) -> &T {
&self.x
}
}
fn main() {
let p1 = Point::new(5, 10); // Point<i32>
let p2 = Point::new(1.2, 3.4); // Point<f64>
println!("p1.x = {}", p1.x());
}
Code language: PHP (php)
ポイント:
impl<T> Point<T>
のように 両方に<T>
を書く
ジェネリクスでも数値だけ指定したい場合は?
ジェネリクスはどんな型でもOKなため、以下のように数値だけ受け取りたくても、文字やbool値を受け取れてしまう
struct Square<T> {
x: T,
y: T,
}
let s1 = Square { x: 1, y: 2 }; // i32
let s2 = Square { x: 1.5, y: 2.3 }; // f64
let s3 = Square { x: "a", y: "b" }; // ❌ 本当は数値以外は避けたいけど作れてしまう
Code language: JavaScript (javascript)
これを解決する方法はいくつかある
自作トレイトを定義する
trait Number {}
impl Number for i32 {}
impl Number for f32 {}
impl Number for f64 {}
impl Number for u32 {}
struct Square<T: Number> {
width: T,
height: T,
}
Code language: CSS (css)
impl Trait for Type
は TypeにTraitを実装する<T: Number>
はTはNumberを実装していなくてはならない
外部クレート(num_traits)を使う
# Cargo.toml
[dependencies]
num-traits = "0.2"
Code language: PHP (php)
use num_traits::Num;
struct Square<T: Num> {
x: T,
y: T,
}
fn main() {
let s1 = Square { x: 3, y: 4 }; // i32 OK
let s2 = Square { x: 2.5, y: 1.5 }; // f64 OK
// let s3 = Square { x: "a", y: "b" }; // ❌ コンパイルエラー
}
Code language: JavaScript (javascript)
コメント