💬
【Rust】トレイト境界~基本編~
トレイト境界
特定の型が特定のトレイトを実装していることをコンパイル時に保証する機能
コンパイラが型の不一致を指摘してくれる
ジェネリクスは型を曖昧に表現する手段です。曖昧に指定するのは場合によっては有効です。
例えば、以下のように「型はどうであれ、数字なら足したい」というシチュエーションです。
fn add_values<T>(a: T, b: T) -> T {
a + b
}
fn main() {
let sum = add_values(5, 10);
println!("Sum: {}", sum);
}
しかし、これはエラーが発生します
error[E0369]: cannot add `T` to `T`
--> src/main.rs:2:9
|
2 | a + b
| - ^ - T
| |
| T
|
= note: no implementation for `T + T`
= help: the trait `std::ops::Add` is not implemented for `T`
コンパイラ曰く「+演算子を使用するには、型TがAddトレイトを実装している必要があります」と言っています。
振る舞いを明示してあげよう
std::ops::Add
を使って、加算することをコンパイラに明示してあげます。
また、T: Add<Output = T>
は加算した結果の型がどうあるべきか まで書いてあげる必要があります。この場合は、加算する値と同じ型です。
use std::ops::Add;
fn add_values<T>(a: T, b: T) -> T
where
T: Add<Output = T>,
{
a + b
}
fn main() {
let sum_int = add_values(5, 10); // i32型
println!("Sum (int): {}", sum_int);
let sum_float = add_values(3.5, 2.7); // f64型
println!("Sum (float): {}", sum_float);
}
まとめ
トレイト境界を使用することで、ジェネリックな型に対して必要なトレイト(ここでは Add
)を実装していることをコンパイル時に保証できます。これにより、以下のような利点があります。
型安全性の向上:必要な操作がサポートされていない型を誤って使用することを防ぎます。
エラーの早期発見:コンパイル時に問題を検出できるため、実行時のバグを減らせます。
コードの汎用性:トレイト境界を適切に指定することで、さまざまな型に対して汎用的な関数や構造体を作成できます。
もちろん、ジェネリックな型を扱うシーン以外でも使用することができます。これはまた改めて記事にしようと思います。
Discussion