[Rustのはじめ方] Part8: 関数定義と戻り値

Rust

プログラムの基本部品、関数の作り方を学ぼう!

プログラミングにおいて、関数は特定のタスクを実行するための一連のコードブロックです。同じ処理を何度も書く代わりに、関数としてまとめておけば、必要な時に呼び出すだけで済みます。これにより、コードが整理され、再利用しやすくなり、メンテナンスも楽になります。

Rustでは、fn キーワードを使って関数を定義します。このステップでは、Rustで関数をどのように定義し、どのように値(戻り値)を返すのかを学びましょう。

Rustの関数定義は fn キーワードから始まります。その後に関数名を続け、括弧 () 内に引数リストを記述します。関数本体は波括弧 {} で囲みます。

基本的な関数の例 (引数なし)

まずは、何も引数を取らず、メッセージを出力するだけの簡単な関数を見てみましょう。

fn say_hello() {
    println!("こんにちは、Rust!");
}

// 関数を呼び出す
fn main() {
    say_hello(); // これで say_hello 関数が実行されます
}

この例では、say_hello という名前の関数を定義しています。main 関数の中で say_hello() と書くことで、この関数を呼び出しています。

引数を持つ関数

関数は、処理に必要な情報を受け取るために引数(ひきすう)を持つことができます。Rustでは、関数の引数を定義する際に、必ず型を明示的に指定する必要があります。

fn greet(name: &str) { // name という名前の引数、型は &str (文字列スライス)
    println!("こんにちは、{}さん!", name);
}

fn main() {
    greet("アリス"); // 文字列 "アリス" を引数として渡す
    greet("ボブ");   // 別の文字列を渡す
}

greet 関数は name という名前の引数を一つ取ります。この引数の型は &str (文字列スライス) です。関数を呼び出すときに具体的な値(例: "アリス")を渡すと、関数内でその値が利用されます。

複数の引数を持つ場合は、カンマ , で区切って記述します。

fn add(x: i32, y: i32) { // x と y、どちらも i32 型
    let sum = x + y;
    println!("{} + {} = {}", x, y, sum);
}

fn main() {
    add(5, 3); // x に 5, y に 3 を渡す
}

関数は、処理の結果として値を返すことができます。これを戻り値(もどりち)と呼びます。Rustでは、戻り値の型を関数の引数リストの後に矢印 -> を使って指定します。

戻り値の型指定と暗黙のreturn

Rustの関数では、関数本体の最後の式(セミコロン ; が付いていないもの)が自動的にその関数の戻り値となります。これは非常に重要な特徴です! ✨

// i32 型の値を返す関数
fn add_one(x: i32) -> i32 {
    x + 1 // セミコロンがないので、この式の結果 (x + 1) が戻り値になる
}

fn main() {
    let five = 5;
    let six = add_one(five); // add_one(5) を呼び出し、戻り値 6 を six に束縛
    println!("5 に 1 を足すと {} です", six); // 結果: 5 に 1 を足すと 6 です
}

add_one 関数は i32 型の値を引数に取り、i32 型の値を返します (-> i32 で指定)。関数本体の最後の x + 1 が評価され、その結果がこの関数の戻り値となります。

return キーワードによる明示的な早期リターン

関数の途中で値を返して処理を終了したい場合は、return キーワードを使うこともできます。

fn check_number(x: i32) -> &str {
    if x > 10 {
        return "10より大きい"; // 条件を満たしたらここで処理を終えて値を返す
    }

    // 上の if が false の場合、この式が評価される
    "10以下" // 関数の最後の式なので、これが戻り値になる
}

fn main() {
    println!("5 は {}", check_number(5));  // 結果: 5 は 10以下
    println!("20 は {}", check_number(20)); // 結果: 20 は 10より大きい
}

ただし、Rustでは多くの場合、最後の式を戻り値とする方が簡潔で好まれます。return は、関数を早期に抜け出す必要がある場合に使うのが一般的です。

戻り値がない関数

値を返さない関数もあります。これまで見てきた println! を呼び出すだけの関数などがそうです。厳密には、Rustでは値を返さない関数は「ユニット型」と呼ばれる空のタプル () を返します。戻り値の型を指定しない場合、自動的に -> () が指定されたものとみなされます。

// これら二つの定義は実質的に同じ意味
fn func1() {
    // 何も返さない
}

fn func2() -> () {
    // 明示的にユニット型を返す
}

Rustの関数定義の構造をまとめると以下のようになります。

要素キーワード/記号説明
関数定義開始fn関数を定義することを示すキーワード。fn greet(...)
関数名(識別子)関数を呼び出すときに使う名前。スネークケース(例: my_function)が慣習。greet
引数リスト(param1: type1, ...)関数が受け取る値(引数)とその型を指定。引数がない場合は ()。型指定は必須。(name: &str)
戻り値の型-> ReturnType関数が返す値の型を指定。省略した場合は () (ユニット型) となる。-> i32
関数本体{ ... }関数の処理内容を記述するコードブロック。最後の式が戻り値となる(セミコロンなしの場合)。{ println!("..."); x + 1 }

Rustのコードは文 (statement)式 (expression) から構成されます。この違いを理解することは、特に関数の戻り値を理解する上で重要です。

  • 文 (Statement): 何らかのアクションを実行しますが、値を返しません。Rustでは、ほとんどの文はセミコロン ; で終わります。例えば、let x = 5; は文です。変数束縛を行い、値は返しません。関数定義 (fn ... {}) も文です。
  • 式 (Expression): 評価されると何らかの値を生成します。例えば、5 + 6 は式で、評価されると値 11 を生成します。関数の呼び出し (add_one(5)) や、ifmatch のブロックなども式になり得ます。

関数本体の波括弧 {} も、それ自体がブロック式 (block expression) となり得ます。ブロック内の最後の式が、そのブロック式全体の値となります。これが、関数の最後の式が戻り値になる理由です。

fn main() {
    let y = { // y にブロック式の結果を束縛
        let x = 3;
        x + 1 // この式の結果 (4) がブロックの値となり、y に束縛される
    }; // ブロック全体が式なので、最後にセミコロンが必要

    println!("y の値は: {}", y); // 結果: y の値は: 4
}

式 (expression) の最後にセミコロン ; を付けると、それは文 (statement) になり、値を返さなくなります(厳密には () を返す)。これが、関数の最後で戻り値としたい式にセミコロンを付けてはいけない理由です。

fn returns_nothing() -> () {
    let _x = 5 + 6; // この式は評価されるが、セミコロンがあるので文になる
    // 最後の式がない、または最後の式がセミコロンで終わる文の場合、() が返る
}

fn returns_eleven() -> i32 {
    5 + 6 // セミコロンがないので、この式の結果 11 が戻り値になる
}

今回はRustの関数の基本について学びました。

  • 関数は fn キーワードで定義します。
  • 引数には必ず型を指定する必要があります。
  • 戻り値がある場合は -> 型 で指定します。
  • 関数本体の最後の式(セミコロンなし)が暗黙的な戻り値になります。
  • return キーワードで早期リターンも可能です。
  • 文と式の違いを理解することが、戻り値の挙動を掴む鍵です。

関数はプログラムを構造化するための基本的な要素です。しっかり理解して、使いこなせるようになりましょう!🚀 次は、Rustの最もユニークな機能の一つである「所有権」について学んでいきます。

コメント

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