グローバルナビゲーションへ

本文へ

フッターへ

お役立ち情報Blog



安全性、速度、並行性を兼ね備えた言語と、巷でうわさの「Rust」を覗いてみる(その4:所有権と関数)

今回も前回に続き、「The Rust Programming Language」を読みながら、所有権と関数を覗いていきたいと思います。

ヒープを使う場合

前回で、シンプルな所有権の動きは理解できたので、関数の場合を見ていきます。

まずは、サンプルのコードを動かしてみます。

fn main() {
    let s = String::from("hello");

    takes_ownership(s); // hello
}

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

最初の takes_ownership 関数に s を渡すところで、sの値が関数(内のsome_string)にムーブされます。

本当にムーブされているか確かめてみます。

fn main() {
    let s = String::from("hello");

    takes_ownership(s);

    println!("{}", s)
}

fn takes_ownership(some_string: String) {
    println!("{}", some_string)
}
error[E0382]: borrow of moved value: `s`
 --> src/main.rs:6:20
  |
2 |     let s = String::from("hello");
  |         - move occurs because `s` has type `String`, which does not implement the `Copy` trait
3 |
4 |     takes_ownership(s); // hellp
  |                     - value moved here
5 |
6 |     println!("{}", s)
  |                    ^ value borrowed here after move

ちゃんとエラーが出ました。

 takes_ownership 関数は値を返さないので、仮引数の some_string は、この関数スコープが終了すると drop 関数がよばれて、 メモリが解放されます。

スタックを使う場合

fn main() {
    let x = 5;

    makes_copy(x); // 5
}

fn makes_copy(some_integer: i32) {
    println!("{}", some_integer)
}
 x は、コンパイル時に決定されるスタックに保存されるデータです。

先ほどと同じく、 makes_copy 関数に x を渡すところで、ムーブされるのですが、 xはスタックに保存されるデータ(Copy)なので、 some_integer に対してdrop関数が呼ばれません。

なので、 makes_copy 関数の後でもxを参照することができるらしいので試してみます。

fn main() {
    let x = 5;

    makes_copy(x); // 5

    println!("{}", x) // 5
}

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

問題なく参照できました。

クロージャの場合

ちょっとイキって教科書から脱線し、クロージャの場合はどうなるのか気になったので試してみます。

fn main() {
    let s = String::from("hello");

    let c = |y| println!("{}", y);

    c(s);

    println!("{}", s);
}
error[E0382]: borrow of moved value: `s`
 --> src/main.rs:8:20
  |
2 |     let s = String::from("hello");
  |         - move occurs because `s` has type `String`, which does not implement the `Copy` trait
...
6 |     c(s);
  |       - value moved here
7 |
8 |     println!("{}", s);
  |                    ^ value borrowed here after move

関数の場合とおなじく、エラーになります。
ここまでは想定通りです。

fn main() {
    let s = String::from("hello");

    let c = || println!("{}", s);

    c();

    println!("{}", s);
}

では、 s をキャプチャした場合の所有権はどうなるのでしょうか。
こちらはエラーになりませんでした。
キャプチャした場合のsはムーブされずに、所有権は main 関数側に残ってそうです。

残念ながら、現在のRust力ではこれ以上の詳細は不明です。
正直、イキり過ぎましたね。

fn main() {
    let s = String::from("hello");

    let c = move || println!("{}", s);

    c();

    println!("{}", s);
}

すこし調べてみたところ、上記のようにクロージャの前に move を付けると、 所有権が移動してエラーになりました。

このへんの詳細は、未来の宿題ということで今はスルーしておきます。

 Fn , FnMut , FnOnce のトレイトがクロージャにあり、使い分けてることをなんとなく記憶しておきます。

この記事を書いた人

tkr2f
tkr2f事業開発部 web application engineer
2008年にアーティスへ入社。
システムエンジニアとして、SI案件のシステム開発に携わる。
その後、事業開発部の立ち上げから自社サービスの開発、保守をメインに従事。
ドメイン駆動設計(DDD)を中心にドメインを重視しながら、保守可能なソフトウェア開発を探求している。
この記事のカテゴリ

FOLLOW US

最新の情報をお届けします