【Rust学習】ジェネリクスを勉強する

Rustを勉強していてジェネリクスの書き方が覚えられなかったので、備忘として記事に残しておこうと思います。

ジェネリクスとは?

ジェネリクスを一言でいうと、

同じコードを、いろんな型に対応できるようにする仕組み

また、ジェネリクスは慣習的にTUが使われる

ジェネリクス付き構造体

固定型の構造体

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>i32f64などとして扱うことができる

ジェネリクス付き関数

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)
  • TU は別々の型
  • 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)

外部クレート(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)

コメント

タイトルとURLをコピーしました