第12章: ジェネリクス
学習目標
- ジェネリクスの概念と利点を理解する
- ジェネリックな関数と構造体を定義する
- 型パラメータの使い方を学ぶ
- ジェネリクスとトレイト境界の組み合わせを理解する
---
12.1 ジェネリクスの基本
12.1.1 なぜジェネリクスが必要か
同じロジックを異なる型で再利用したい場合:
// ジェネリクスなしの場合
fn largest_i32(list: &[i32]) -> &i32 {
let mut largest = &list[0];
for item in list {
if item > largest {
largest = item;
}
}
largest
}
fn largest_char(list: &[char]) -> &char {
let mut largest = &list[0];
for item in list {
if item > largest {
largest = item;
}
}
largest
}
// ジェネリクスを使用
fn largest<T: PartialOrd>(list: &[T]) -> &T {
let mut largest = &list[0];
for item in list {
if item > largest {
largest = item;
}
}
largest
}
fn main() {
let numbers = vec![34, 50, 25, 100, 65];
println!("最大値: {}", largest(&numbers));
let chars = vec!['y', 'm', 'a', 'q'];
println!("最大: {}", largest(&chars));
}
12.1.2 型パラメータの命名規則
慣例として、短い名前を使用:
T- Type(一般的な型)E- Error(エラー型)K,V- Key, Value(キーと値)R- Result(結果型)
12.2 ジェネリックな構造体
12.2.1 単一の型パラメータ
struct Point<T> {
x: T,
y: T,
}
fn main() {
let integer_point = Point { x: 5, y: 10 };
let float_point = Point { x: 1.0, y: 4.0 };
println!("整数: ({}, {})", integer_point.x, integer_point.y);
println!("浮動小数点: ({}, {})", float_point.x, float_point.y);
}
12.2.2 複数の型パラメータ
struct Point<T, U> {
x: T,
y: U,
}
fn main() {
let mixed = Point { x: 5, y: 4.0 };
println!("混合: ({}, {})", mixed.x, mixed.y);
}
12.3 ジェネリックなメソッド
12.3.1 implブロックでのジェネリクス
struct Point<T> {
x: T,
y: T,
}
impl<T> Point<T> {
fn x(&self) -> &T {
&self.x
}
fn y(&self) -> &T {
&self.y
}
}
// 特定の型に対してのみ実装
impl Point<f64> {
fn distance_from_origin(&self) -> f64 {
(self.x.powi(2) + self.y.powi(2)).sqrt()
}
}
fn main() {
let p = Point { x: 3.0, y: 4.0 };
println!("距離: {}", p.distance_from_origin());
}
12.4 ジェネリクスとトレイト境界
12.4.1 トレイト境界による制約
use std::fmt::Display;
fn print_pair<T: Display, U: Display>(a: T, b: U) {
println!("({}, {})", a, b);
}
// where句を使った記法
fn print_pair_where<T, U>(a: T, b: U)
where
T: Display,
U: Display,
{
println!("({}, {})", a, b);
}
fn main() {
print_pair(1, "hello");
print_pair_where(3.14, 'x');
}