Zenn
‼️

【Rust】ジェネリクスを使いこなして型の柔軟性を手に入れろ!

2025/03/22に公開

https://doc.rust-lang.org/book/ch10-01-syntax.html

Rustのジェネリクスは、型安全性を保ちながらコードの再利用性を高めるために利用される機能です。ジェネリクスを使うことで、同じロジックを異なる型に対して適用することが可能になります。

この記事ではジェネリクスの基本的な使い方について解説します。
「The Rust Programming Language」を読んでもしっくり来ていないあなた、確実にこの記事で理解し、使いこなせるようになります。

ジェネリクスとは

ジェネリクスとは、具体的な型を指定せずに抽象的に型を表現する方法です。これにより、特定の型に依存しない柔軟なコードを書くことができます。

例えば、2つの数値を足し算する場合を考えてみましょう。

ジェネリクスを使わない場合

fn add_i32(a: i32, b: i32) -> i32 {
    a + b
}

fn add_f64(a: f64, b: f64) -> f64 {
    a + b
}

足し算する関数だけど、型が i32f64 で違うから別の関数を用意したけど一緒にしたい。こういう時にジェネリクスを使うと型を抽象化して1つの関数で済ませることができます。

ジェネリクスを使う場合

fn add<T: std::ops::Add<Output = T>>(a: T, b: T) -> T {
    a + b
}

これで、i32 でも f64 でも、1つの関数で処理できるようになりました。

ジェネリクスの構文を理解する

fn add<T: std::ops::Add<Output = T>>(a: T, b: T) -> T
  • add<T>
    • ジェネリクス関数であることを示しています。
  • T: std::ops::Add
    • 「型Tが加算演算子 + を使えること」を制約として示しています。
  • <Output = T>
    • 加算の結果の型をTと同じ型に指定しています。
  • (a: T, b: T)
    • 関数の引数をジェネリックな型 T で定義しています。
  • -> T
    • 戻り値の型も、引数と同じ抽象的な型 T として指定しています。

あらゆる構文でジェネリクスを使用する

1.関数

fn identity<T>(x: T) -> T {
    x
}

2. 構造体

struct Point<T> {
    x: T,
    y: T,
}

let p1 = Point { x: 10, y: 20 };
let p2 = Point { x: 1.2, y: 3.4 };

3. 列挙型

enum Result<T, E> {
    OK(T),
    Err(E),
}

fn divide(a: f64, b: f64) -> Result<f64, String> {
    if b == 0.0 {
        Result::Err("Cannot divide by zero.".to_string())
    } else {
        Result::Ok(a / b)
    }
}

4. トレイト

trait Summary<T> {
    fn summarize(&self) -> T;
}

struct Book {
    title: String,
}

impl Summary<String> for Book {
    fn summarize(&self) -> String {
        format!("Title: {}", self.title)
    }
}

まとめ

  • ジェネリクスとを使うとコードの再利用性が向上する
  • 柔軟で抽象的な設計が可能になる
  • どんな構文でも利用可能

Discussion

ログインするとコメントできます