🔧
関数とメソッド — defがfnになると何が変わるか
戻り値の型、セミコロン、そしてRubyと同じ「最後の式が戻り値」ルール
Rubyでは関数定義で引数の型も戻り値の型も書かない。Rustは両方必須。
# Ruby
def greet(name)
"Hello, #{name}!"
end
// Rust
fn greet(name: &str) -> String {
format!("Hello, {}!", name)
}
->の後に戻り値の型を書く。戻り値なし(Rubyのnil返却)は-> ()だが省略可能。
セミコロン = 戻り値かどうか
RubyもRustも「最後の式が戻り値」というルールがある。ただしRustではセミコロンを付けるとstatementになり、戻り値ではなくなる。
fn add(a: i32, b: i32) -> i32 {
a + b // セミコロンなし → 戻り値
}
fn add_broken(a: i32, b: i32) -> i32 {
a + b; // セミコロンあり → statement、()を返す → コンパイルエラー!
}
これは最初本当に混乱する。Rubyではセミコロンは文の区切りでしかなく意味がないが、Rustでは戻り値の有無を決定する。
early return
Rubyと同じくreturnで早期リターンできる。
fn check_age(age: u32) -> &'static str {
if age < 18 {
return "minor"; // early returnにはセミコロン必要
}
"adult" // 最後の式はセミコロンなし
}
returnにはセミコロンを付ける。最後の式での戻りには付けない。これがRustの慣例。
引数の渡し方 — 値 vs 参照
Rubyでは引数の渡し方を気にする必要がない。Rustでは所有権の問題で借用するか(borrow)、渡すか(move)を決定する必要がある。
// 借用 — 元のデータ維持
fn print_name(name: &str) {
println!("{}", name);
}
// 所有権移動 — 呼び出し後に元のデータ使用不可
fn take_name(name: String) {
println!("{}", name);
}
let name = String::from("sehwa");
print_name(&name); // 借用、nameはまだ使える
take_name(name); // 移動、以降nameは使用不可
大抵の場合&で借用すればいい。「読むだけなのに所有権まで渡す必要はない」という判断。
クロージャ — インライン関数
Rubyのブロック/lambdaがRustではクロージャ。関数の引数として渡すパターンがほぼ同じ。
# Ruby
[1, 2, 3].map { |x| x * 2 }
// Rust
vec![1, 2, 3].iter().map(|x| x * 2).collect::<Vec<_>>();
|x|がRubyの{ |x| }に対応する。波括弧の代わりにパイプ(|)で引数を囲む。
キーポイント
1
fn 名前(引数: 型) -> 戻り値型 { } の形式で定義
2
セミコロンなし = 戻り値、あり = statement — これが核心
3
early returnにはreturnキーワード + セミコロンを使用
4
引数は&で借用するか所有権を移動する
メリット
- ✓ 関数シグネチャだけで入出力の型がわかる
- ✓ Rubyと同じ「最後の式が戻り値」ルールで適応が速い
デメリット
- ✗ セミコロンのミスでコンパイルエラーが出るのが初期に頻発する
- ✗ 借用/所有権の決定がRubyにない概念なので関数設計が難しい
ユースケース
Rubyのメソッドチェーンをrust関数の組み合わせに変換する時
コールバックパターンをクロージャで実装する時