ジェネリック関数
CoddyのRustジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 27/61。
ジェネリックは構造体に限られません。任意の型で動作するスタンドアロン関数も記述できます。これは、特定の構造体に属さないものの、柔軟性を維持する必要があるユーティリティ関数が必要な場合に役立ちます。
syntax は、構造体で見たものとよく似ています。function 名の後の山括弧内で generic パラメータを宣言し、その後 parameters と return 型で使用します:
fn identity<T>(value: T) -> T {
value
}
この identity 関数は任意の型の値を受け取り、変更せずに返します。<T> は generic を宣言し、value: T はそれをパラメーターとして受け取り、-> T は戻り値の型を指定します。関数を呼び出すと、Rust は具体的な型を推論します。
let num = identity(42); // T は i32
let text = identity("hello"); // T は &str
構造体の場合と同様に、関数で複数のジェネリックパラメータを使用することもできます。
fn make_pair<T, U>(first: T, second: U) -> (T, U) {
(first, second)
}
let pair = make_pair(10, "ten"); // (i32, &str) を返す
ジェネリック関数を使うと、再利用可能なロジックを一度だけ記述して多くの型に適用できるため、型安全性を維持しながらコードの重複を減らせます。
チャレンジ
簡単任意の型で動作する generic 関数を備えたユーティリティモジュールを作成しましょう!特定の構造体に縛られることなく、generic によってコードを柔軟かつ再利用可能にする方法を示す、独立した function を作成します。
コードを2つのファイルに分けて整理します。
utils.rs: public な generic ユーティリティ function のコレクションを作成します。wrap_in_pair<T>: 単一の値を受け取り、その値を2回含むタプルを返します:(value, value)。これには型がCloneであることが必要なので、<T: Clone>を使用します。swap<T, U>: potentially 異なる型の2つの値を受け取り、それらを reversed 順序のタプル(U, T)として返します。
main.rs: ユーティリティモジュールを import し、これらの generic function が different な型で動作することを示します。同じ function 定義で integer、float、string をシームレスに処理できることを示してください。
main ファイルでは、次のようにユーティリティ function を示します。
- integer(first input、
i32として parsed)でwrap_in_pairを使用し、両方の要素を出力する - string(second input)で
wrap_in_pairを使用し、両方の要素を出力する - integer(third input、
i32として parsed)と string(fourth input)でswapを使用し、swapped result を出力する
出力は次の Format に従う必要があります。
Pair of ints: ({value}, {value})
Pair of strings: ({value}, {value})
Swapped: ({string}, {int})たとえば、inputs が 5、hello、42、world の場合:
Pair of ints: (5, 5)
Pair of strings: (hello, hello)
Swapped: (world, 42)wrap_in_pair が integer と string の両方で同じように動作し、swap が完全に different な2つの型を処理していることに注目してください。これが generic function の柔軟性です。
4つの input を受け取ります。integer、string、別の integer、そして別の string です。
自分で試してみよう
mod utils;
use std::io;
fn main() {
// 4つの入力を読み取る
let mut input1 = String::new();
io::stdin().read_line(&mut input1).expect("Failed to read line");
let num1: i32 = input1.trim().parse().expect("Invalid integer");
let mut input2 = String::new();
io::stdin().read_line(&mut input2).expect("Failed to read line");
let str1 = input2.trim().to_string();
let mut input3 = String::new();
io::stdin().read_line(&mut input3).expect("Failed to read line");
let num2: i32 = input3.trim().parse().expect("Invalid integer");
let mut input4 = String::new();
io::stdin().read_line(&mut input4).expect("Failed to read line");
let str2 = input4.trim().to_string();
// TODO: wrap_in_pair を num1 で使用し、結果を出力する
// Format: Pair of ints: ({value}, {value})
// TODO: wrap_in_pair を str1 で使用し、結果を出力する
// Format: Pair of strings: ({value}, {value})
// TODO: swap を num2 と str2 で使用し、交換した結果を出力する
// 形式: 交換済み: ({string}, {int})
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
自分で練習してみよう: Rustオンラインコンパイラ