5
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

関数

関数の宣言はfnを用います。引数がある場合はfn foo(bar: i32)のように()の中に引数名とその型をペアにして列挙します。
戻り値は-> <型>の形で宣言します(戻り値が無い場合は省略できます)。

Rustの特徴として、関数の最後にreturnを明示的に書かなくても値を返すことができます。Rustでは文末にセミコロンがないものを「式」と呼び、何らかの値に評価されます。逆に、セミコロンが付いている場合はそれが「文」となり、値に評価されません。関数やブロックの最後にセミコロンを付けない「式」を書くと、その値がその関数やブロックの戻り値として扱われます。そのため、関数の最後に戻り値を示す式(セミコロンを付けない形)を書くことで、returnを使う場合と同じように値を返せます(もちろん、return文を明示的に使うことも可能です)。

fn add(a: i32, b: i32) -> i32 {
    a + b
}

fn main() {
    let result = add(5, 3);
    println!("The sum is: {}", result);
}

TypeScriptと違い、関数の戻り値は型推論されないので、明示的に指定する必要があります。

// `:number`が無くても暗黙的にnumberを返す関数と推論される
const multiply = (x: number, y: number) => {
    return x * y;
};
// `-> i32`は省略できない
fn multiply(x: i32, y: i32) -> i32 {
    x * y
}

スコープ

ブロック{}内で宣言された変数は、そのブロックの中でしか利用できません。この有効範囲を「スコープ」と呼びます。
ある変数のスコープを抜けると、その変数のメモリは解放され、もうアクセスすることはできません。

fn main() {
    {
        let x = 42; // xはこのスコープ内でのみ有効
        println!("x: {}", x);
    }
    // println!("x: {}", x); // エラー: xはスコープ外
}

引数のコピー

関数が引数を取る場合、その引数は型によってコピーされるかどうかが決まります。i32boolのような型の引数の場合、コピーされて関数に渡されます。一方で、Stringや独自の構造体のような型の引数の場合、コピーされません。

fn takes_ownership(s: String) {
    println!("{}", s);
}

fn makes_copy(n: i32) {
    println!("{}", n);
}

fn main() {
    let s = String::from("hello");
    takes_ownership(s); // sの所有権が関数に移動
    // println!("{}", s); // エラー: sはもう使えない

    let n = 42;
    makes_copy(n); // nはコピーされる
    println!("{}", n); // nはそのまま利用可能
}

クロージャ

クロージャは関数の一種で、定義外の変数を捕捉することが可能です。記法が通常の関数宣言と異なっており、()の代わりに||を利用します。ブロック部分も、式が一つだけでそれをそのまま戻り値として使うのであれば{}も不要になります。
また、変数にバインディングすることができます。

fn main() {
    let add = |a: i32, b: i32| a + b;
    println!("5 + 3 = {}", add(5, 3));

    let x = 10;
    let print_x = || println!("Captured x: {}", x);
    print_x();
}

クロージャに関しては強力な型推論が働きます。通常の関数宣言と異なり、型アノテーションは省略することができます(引数名は省略できません)。

fn main() {
    let square = |n| n * n;
    println!("Square of 4: {}", square(4));
}

このクロージャという仕組みはTypeScriptでいうアロー関数と似た使用感を持っています。

const add = (a: number, b: number) => a + b;
console.log(`5 + 3 = ${add(5, 3)}`);

筆者の感想ですが、アロー関数は単に記法の話で無名関数として使うことが多い一方、Rustでは明確に「スコープ外の変数を利用できる」という特徴を持たせているのが印象的です。

また、引数の型アノテーションまで不要なのに驚きました。

クロージャがスコープ外の変数を参照するとき、以下のどれかの方法で参照します。基本的には(イミュータブルな)参照を取得しようとし、場合に応じてミュータブルな参照もしくは値そのものを取得します。

値そのものを取得するとき、それがコピー可能であればコピーします。コピー不可能である場合、所有権そのものをクロージャに移動します。moveキーワードを使うと、所有権の移動を強制できます。

fn main() {
    let mut x = 5;

    let immut_closure = || println!("x: {}", x); // 共有参照
    immut_closure();

    let mut_closure = || {
        x += 1; // 可変参照
        println!("x after mutation: {}", x);
    };
    mut_closure();

    let move_closure = move || {
        println!("x moved: {}", x); // 所有権移動
    };
    move_closure();
}
5
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?