今回はたぶんメッチャ大事な回。
成果物
参考
参照と借用
所有権のムーブで使えなくなるケース
fn main() { let s = String::from("hello"); let len = calc_len(s); // sの所有権がmain関数スコープのsからcalc_len関数スコープのsへムーブ println!("{}.", s1); // 所有権がないので参照不可! } fn calc_len(s: String) -> usize { s.len() } // sは所有権をもつスコープの末端に来たので無効化(メモリ解放)
参照(借用)にて所有権のムーブをさせない
fn main() { let s = String::from("hello"); let len = calc_len(&s); // &で参照する(借用) println!("{}.", s1); // 所有権はmain関数のままなので参照可 } fn calc_len(s: &String) -> usize { s.len() }
借用すればcalc_len関数の終わりでメモリ解放させずにs変数を利用できる。
参照
参照とは&
記号である。所有権をムーブすることなく変数の値を参照できる機能。ただし変更はできない。
読取専用のポインタみたいなものだと思われる。
借用
借用とは、関数の引数に参照をとること。
可変な参照
let mut s = String::from("hello"); let r1 = &mut s;
mut
でmutable(可変)にできる。
可変参照は1つのみ
let mut s = String::from("hello"); let r1 = &mut s; let r2 = &mut s; // error[E0499]: cannot borrow `s` as mutable more than once at a time
生きている可変参照は1つでなければならない。上記はエラー。
スコープが終わった後なら問題ない。以下はOK。
let mut s = String::from("hello"); { let r1 = &mut s; } // r1はここでスコープを抜けて無効化(メモリ解放)されるので、以降はsの可変参照を作れる let r2 = &mut s;
また、不変で借用されているのに可変で借用しようとしたらエラーになる。これは不変であることを保証するためなので自然。
let mut s = String::from("hello"); let r1 = &s; // OK let r2 = &s; // OK let r3 = &mut s; // error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable
これらの制約は既存のメモリ操作に慣れたプログラマには苦労する原因になりそう。
ダングリングポインタの防止
メリットもある。
ダングリングポインタとは無効なメモリ領域を指すポインタのこと。別の箇所で解放されたメモリ領域を、解放されたと知らずに参照してしまうポインタのこと。
C/C++では意図せず発生してしまいうるが、Rustでは起こりうるコードを書くことはできずコンパイルエラーになる。
fn main() { let reference_to_nothing = dangle(); } fn dangle() -> &String { // error[E0106]: missing lifetime specifier let s = String::from("hello"); &s } // 変数s無効化(メモリ解放)
「ライフタイム指定子がない」という謎のエラー。それは別の章でやるらしいので無視。重要なのは変数s
のスコープ。dangle
関数で宣言したので同関数が終わると解放される。その前に解放された変数のポインタを返している。つまり、無効なメモリ領域を指すポインタを返してしまっている。
Rustではコンパイルエラーになるため、動作すらさせず未然に危険なメモリアクセスを防いでくれる。これがもしC/C++なら実行時に強制終了されることになるはず。
解決するにはString型変数を返せばいい。関数で戻り値を返すことは所有権のムーブがなされる。よってメモリ解放は戻り値を受け取った側のスコープ終端になる。
fn dangle() -> String { let s = String::from("hello"); s }
対象環境
- Raspbierry pi 3 Model B+
- Raspbian stretch 9.0 2018-11-13
- bash 4.4.12(1)-release
$ uname -a Linux raspberrypi 4.19.42-v7+ #1219 SMP Tue May 14 21:20:58 BST 2019 armv7l GNU/Linux
前回まで
- Rustを学んでみたい(プログラミング言語)
- Rustの環境構築
- RustでHelloWorld
- Rustの和訳ドキュメント
- Cargoでプロジェクト作成・ビルド・実行
- クレートとは?
- Rustで関数を使ってみる
- Rustでモジュールを使ってみる
- Rustで乱数を生成する(rand)
- Rustで標準入力する(std::io::stdin().read_line())
- RustでMatch判定する(match)
- Rustでprintとread_lineを1行にする方法
- Rustで数当てゲーム
- クレート名にドット.が使えない
- Rustの変数と可変性(let, mut) error[E0384]: cannot assign twice to immutable variable
x
- Rustのimmutable束縛とconst定数の違い
- RustのREPL、evcxrのインストールに失敗した
- Rustでコンパイルするときの変数未使用warningを消す
- Rustの変数(再代入、再宣言(シャドーイング))
- Rustのシャドーイングについて
- イミュータブルについて(副作用)
- Rustの定数(const)
- Rustのデータ型(数値)
- Rustのデータ型(論理)
- Rustのデータ型(文字)
- Rustのデータ型(タプル)
- Rustのデータ型(配列)
- Rustの関数
- Rustのif式
- Rustのくりかえし文(loop)
- Rustのくりかえし文(while)
- Rustのくりかえし文(for)
- Rustの所有権(ムーブ)