ジェネリック関数
型によって振る舞いをパラメーター化します。
「ジェネリック関数」はCoddyKit上の無料Learn Rust Codingレッスンです。 これはレッスン1/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはLearn Rust Coding学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Learn Rust Codingコースには全4レッスンが含まれています。
ジェネリック関数を使う理由
ジェネリック関数を使うと、型ごとにコードをコピーせず、さまざまな型で使える1つの関数を書けます。Rustはコンパイル時にプレースホルダーの型を置き換えます。
整数用のlargestと文字用の別のlargestを書くことを考えてみてください。ジェネリクスを使えば、それらを実行時コストなしの1つの定義にまとめられます。
型パラメータ
ジェネリック関数では、名前の後ろにある山かっこの中で型パラメータを宣言します。Tという名前は慣例ですが、CamelCaseの識別子なら何でも使えます。
このパラメータは引数や戻り値の型に記述でき、呼び出し側が使う具体的な型の代わりになります。
fn first<T>(pair: (T, T)) -> T {
pair.0
}単純なIdentity関数
このプログラムでは、引数を変更せずに返すジェネリックなechoを定義します。整数で1回、文字列スライスで1回呼び出します。
コンパイラーは、実際に使われる型ごとに具体化されたバージョンを生成します。
fn echo<T>(value: T) -> T {
value
}
fn main() {
println!("{}", echo(42));
println!("{}", echo("hi"));
}単相化
Rustは単相化を使用します。コンパイル時に、使われる具体的な型ごとに関数の特化されたコピーを生成します。ボックス化や仮想ディスパッチは行われません。
その結果、ジェネリックではない型専用の手書きコードと同じ速さで実行されるジェネリックコードになります。
ジェネリクスには制約が必要
ジェネリック関数内では、あらゆる可能性のある型がサポートする操作だけを使えます。単なるTに対して、加算、比較、出力を行うことはできません。
このコードは、Tが比較を実装していない可能性があるためコンパイルに失敗します。次はtrait boundを使って修正します。
fn larger<T>(a: T, b: T) -> T {
if a > b { a } else { b }
}Trait Boundの追加
値を比較するため、PartialOrdによってTに制約を付けます。このboundにより、その型が>演算子をサポートすることを保証します。
また、整数のような単純な型で移動に関する問題を起こさず値を返せるよう、Copyも追加します。
fn larger<T: PartialOrd + Copy>(a: T, b: T) -> T {
if a > b { a } else { b }
}Bound付き関数の呼び出し
これで、この関数は両方のtraitを実装するあらゆる型で動作します。整数と浮動小数点数は、どちらもPartialOrd + Copyを満たします。
同じソースコードが2つのバージョンに単相化され、それぞれの呼び出し結果を出力します。
fn larger<T: PartialOrd + Copy>(a: T, b: T) -> T {
if a > b { a } else { b }
}
fn main() {
println!("{}", larger(3, 9));
println!("{}", larger(2.5, 1.0));
}複数の型パラメータ
関数では複数の型パラメータを宣言できます。それぞれは独立しているため、2つの引数に異なる型を指定できます。
ここでのpairは、任意のTと任意のUを受け取り、それらをタプルとして返します。
fn pair<T, U>(a: T, b: U) -> (T, U) {
(a, b)
}ジェネリックな値の出力
{}を使ってジェネリックな値を出力するには、その型がDisplayを実装している必要があります。出力可能な型を渡せるよう、このboundを追加します。
これは値にラベルを付けて返すもので、整形だけを目的にboundを使う例です。
use std::fmt::Display;
fn announce<T: Display>(label: &str, value: T) {
println!("{}: {}", label, value);
}
fn main() {
announce("count", 7);
announce("name", "Ada");
}Turbofish構文
引数だけではコンパイラーがTを推論できないことがあります。Turbofishの::<Type>を使うと、呼び出し側で明示的に指定できます。
戻り値の型が曖昧になるparseやcollectなどのメソッドで、特によく使われます。
fn main() {
let n = "42".parse::<i32>().unwrap();
println!("{}", n + 1);
}参照に対するジェネリクス
ジェネリックパラメータは参照にも使えます。参照に対して制約を付けると、値を読むだけの場合にCopyを要求せずに済みます。
このlongest_str風のヘルパーは2つのスライスを借用し、所有権を取得せずにそのうちの1つを返します。
fn pick<'a, T: PartialOrd>(a: &'a T, b: &'a T) -> &'a T {
if a > b { a } else { b }
}クイックチェック
Rustのジェネリック関数についての理解度を確認しましょう。
まとめ
ジェネリック関数は山かっこの中に型パラメータを記述し、単相化によって実行時コストなしでさまざまな型に対応します。
ジェネリック型に対する操作には、PartialOrd、Copy、Displayなどのtrait boundが必要です。型を推論できない場合はTurbofishを使います。
よくある質問
「ジェネリック関数」レッスンは無料ですか?
はい。「ジェネリック関数」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Learn Rust Codingコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Learn Rust Codingコースには全4レッスンが含まれています。
「ジェネリック関数」で何を学びますか?
型によって振る舞いをパラメーター化します。 ブラウザで直接実行するハンズオンコードでLearn Rust Codingを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Learn Rust Codingを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのLearn Rust Codingは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン1/4です。
「ジェネリック関数」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このLearn Rust Codingレッスンでコードを書いて実行できますか?
はい。すべてのLearn Rust Codingレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。