関数

関数を定義するにはfnキーワードを使います。構文はfn function_name<T>(arg1: T, arg2: T) -> T { ... }のような形です。戻り値の型が()の場合はそれを省略できます。

戻り値には;セミコロンを付けずに式のままにしておきます。もし、途中で早期リターンさせたい場合の為にreturn 式;も用意されてます。

fn plus(left: i32, right: i32) -> i32 {
  left + right
}

assert_eq!(plus(1, 2), 3);

<T>のようなジェネリックを指定することで、例えばTrait std::ops::Addが実装されているすべてでは無い複数の型に制限する事も可能です。

fn plus<T: std::ops::Add>(left: T, right: T) -> T::Output {
  left + right
}

assert_eq!(plus(1, 2), 3);
assert_eq!(plus(1.2f32, 2.8f32), 4.0f32);

もしジェネリック部分が長くなってしまうならwhereの後ろに置くこともできます。

fn foo<T>(v: &T)
where
  T: ?Sized + core::fmt::Debug,
{
  println!("{:?}", v);
}

?Sized

?Sizeはサイズ不定型と呼ばれる、実行時にどれくらいメモリを使うか分からないものの事です。基本的にこれはSized + 参照どちらも渡せる意味になります。

fn foo<T>(v: &T)
where
  T: ?Sized + core::fmt::Debug,
{
  println!("{:?}", v);
}

foo("str");
// "str"

foo(&"str".to_string());
// "str"

foo(&[1, 2, 3]);
// [1, 2, 3]

foo(&vec![0; 10]);
// [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

foo(&A {
  value: "value".into(),
});
// A { value: "value" }

クロージャー

以下のように変数に直接関数のようなものを代入することができます。ブロック{ }を使わない場合戻り値の型は書けません。

let plus_one = |n: i32| n + 1;
//
// 以下のようなもの
//
// fn plus_one(n: i32) -> i32 {
//  n + 1
// }

2行以上になる場合はブロック{ }で囲みます。こちらは戻り値の型を書けますが省略可能です。

let multiply_three_plus_one = |n: i32| -> i32 {
  let mut num = n * 3;
  num += 1;
  num
};

assert_eq!(multiply_three_plus_one(2), 7);

クロージャーで注意なのが現在のスコープに存在する値を暗黙的に借用か所有権を移動させる事です。

let str = String::from("foo");

let _add_bar = || str + "baz";

println!("{}", str); // error

Struct std::string::Stringは、Copyトレイトを実装していない為その所有権はクロージャーの中のstrに渡されます。それにより元々のstrは使えなくなってしまうので、println!でのstr仕様はエラーとなってしまいます。

Copyトレイトを実装していれば値はコピーされて使われるので上記のようなエラーは起きません。Copyトレイとは例えばi32などが実装しています。

let one = 1;

let _plus_one = || one + 1;

println!("{}", one); // 1

mutなクロージャーは変更可能な参照(&mut)を束縛します。

let mut num = 5;

{
    let mut add_num = |x: i32| num += x;
    add_num(5);
}

println!("{}", num); // 10

必ずコピーした所有権を渡したい場合moveキーワードをクロージャーの前に付けます。

let mut num = 5;

{
    let mut add_num = move |x: i32| {
        num += x;
        
        println!("{}", num); // 10
    };

    add_num(5);
}

println!("{}", num); // 5

参照の場合も大丈夫です。

let one_two_three = &[1, 2, 3];

let _push_four = || {
    let mut v: Vec<i32> = Vec::new();

    v.extend(one_two_three);
    v.push(4);

    v
};

println!("{:?}", one_two_three);