今日の内容
- ジェネリクス
はじめに
前回は、BlackJackを完成させました。しかしながら、このシリーズの軌道が少しずれてきていた気がしたので、いったん戻ります!
今回は ジェネリクス について学びます。
ジェネリクスとは
ジェネリクス は、「特定の型に依存しないコードを書く」ための仕組みです。
Rustでは、ジェネリクスはプレースホルダのように使われ、コードを書くときには型を指定せず、コンパイル時に具体的な型が決まります。
この機能はどのような場面で使えるでしょうか?
関数で異なる型の引数を処理したり、構造体や列挙型で任意の型を格納する時などに使えます。
関数でのジェネリクス
ジェネリクスを使うと、異なる型の引数でも処理できる汎用的な関数を作成できます。
fn get_larger<T: PartialOrd>(a: T, b: T) -> T {
if a > b {
a
} else {
b
}
}
fn main() {
println!("{}", get_larger(10, 20));
println!("{}", get_larger(1.2, 3.4));
}
- : ジェネリック型を定義
- T: PartialOrd: トレイトを実装してTが比較可能であることを指定
ここでのポイントは、ジェネリクスは 型安全 で、どの型にも対応するわけではありません。
必要に応じて トレイト境界 を追加して型の制約を設定できます。
構造体でのジェネリクス
ジェネリクスを使って、構造体のフィールドに複数の型を持たせることができます。
struct Point<T> {
x: T,
y: T,
}
fn main() {
let integer_point = Point { x: 1, y: 2};
let float_point = Point { x: 3.0, y: 4.0};
println!("Integer Point: ({}, {})", integer_point.x, integer_point.y);
println!("float Point: ({}, {})", float_point.x, float_point.y);
}
/******** 実行結果 ********
Integer Point: (1, 2)
float Point: (3, 4)
*************************/
-
<T>
は任意の型を表す -
Point<T>
はどの型でも扱える汎用的な構造体
列挙型でのジェネリクス
列挙型もジェネリクスを使って柔軟に設計できます。
fn main() {
let some_number = Some(5);
let some_string = Some("Hello");
println!("{:?}", some_number);
println!("{:?}", some_string);
}
/******** 実行結果 ********
Some(5)
Some("Hello")
*************************/
Rust標準ライブラリの Option型 はジェネリクスを活用した代表的な例です。
Option型は次回学びます。
トレイト境界
ジェネリクスを使う場合、ある特定のトレイトを実装した型だけを許可することができます。これを トレイト境界 と呼びます。
fn print_display<T: std::fmt::Display>(value: T) {
println!("{}", value);
}
fn main() {
print_display(123);
print_display("Hello");
// print_display(vec![1, 2, 3]);
}
/******** 実行結果 ********
123
Hello
*************************/
「123」という整数や「Hello」というような文字列はDisplay
を実装していますが、Vecは実装してないので、print_display(vec![1, 2, 3]);
はエラーになります。
おわりに
今回はRustでのジェネリクスについて学びました。
次回は、Option型について学ぼうと思います。
ご精読ありがとうございました。