The Rust Programming Language ノート
The Rust Programming Language 2nd ED. からのメモ書き。
Resource Acquisition Is Initialization
RAII は GC に頼らない Rust のメモリ管理の方針。リソース (メモリ領域) の確保と同時に初期化し、スコープから外れると同時に開放するために、リソースを所有している変数を明確に意識しなければならない。
Ownership (所有者)
有効なリソース (値を表しているメモリ領域) はその所有者となる変数が 1 つだけ存在する。参照の代入は所有権のムーブを意味している。
let s0 = "hello"; let s1 = s0; println!(s0);error: expected a literal --> src\main.rs:17:12 | 3 | println!(s0); | ^^上記で expected a literal は宣言されていない変数を使用したときのコンパイルエラーメッセージ。つまり
s1にムーブした時点で元のs0のスコープは終了している。スコープ
リソースはその所有者となっている変数がスコープから外れることで削除される。これはスタックに保存されたリソースでも
Box::new()を使ってヒープ上に保存されたリソースでも同じ。デストラクタが定義されている場合はスコープ終了時にデストラクタが呼ばれる。{ let s0 = "hello"; // スタックにリソースを確保 { let s1 = Box::new(Foo()); // ヒープにリソースを確保 // ヒープ上の s1 はここでデストラクタが呼ばれ開放される } // スタック上の s0 はここで開放される }構造体に対するデストラクタ実行後、それぞれのフィールドに対するデストラクタの呼び出しが行われる。
コピーとムーブ
u32のようなプリミティブ型の代入やCopyトレイトの継承した構造体は単純なバイトレベルでのコピーで行われる。参照は代入は「コピー」と「ムーブ」{ let s = "hello"; }Borrowing (借用)
Ownership を変更せず一時的に値を使用する。
{ let s = "hello"; }デストラクタ
Dropトレイトを継承するとデストラクタを定義できる。リソースは所有者のスコープが終了する時点で (それがスタックに確保されたリソースかヒープに確保されたリソースかにかかわらず) 暗黙的にデストラクタが呼び出される。struct Foo { bar: u32 } impl Drop for Foo { fn drop(&mut self) { println!("drop()") } } let foo = Box::new(Foo { bar: 100 }); println!("Foo.bar = {}", foo.bar);Foo.bar = 100 drop()
変数
let (不変変数)
変数はデフォルトで不変 (再代入不可)。
letで宣言すると同時に代入を行わなければならない。let x = 0; x = 1;1 | let x = 0; | - first assignment to `x` 2 | x = 1; | ^^^^^ cannot assign twice to immutable variablelet mut (可変変数)
変数を可変 (mutable; 再代入可能) にするには
mutを付加する。let mut x = 0; println!("x = {}", x); // => x = 0 x = 1; println!("x = {}", x); // => x = 1const (定数)
定数には必ず型注釈を記述しなければならない。定数はグローバルを含むどのようなスコープでも使用することができる。定数の右辺値は定数式に限定される (コンパイル時に決定できない値 ─ 任意の計算結果や関数の返値は指定できない)。
const X:f64 = 1.0; const Y:f64 = X * 2.0; // const Z:f64 = X.ln(); // => calls in constants are limited to struct and enum constructorsただし、将来的にはコンパイル時点の関数の呼び出し結果を使用する
const fnが準備される (現在は Nightly 版コンパイラのみ)。シャドーイング
letは同一スコープ内に同じ名前を持つ変数を定義することができる。mutと異なり、割り当てられるのは本質的に別の変数であるため異なる型を使用することができる。let x = 1; let x = 2 * x; let x = x * x; println!("x = {}", x); // => x = 4 let x = format!("[{}]", x); println!("x = {}", x); // => x = [4]
エラー処理
? (question operator)
Resultに対する?(クエスチョン演算子) は結果がErrであればその場で return する。従ってResultを返す関数であればand_thenでネストしなくてもエラーが起きた時点で処理を中断することができる。fn f() -> Result<A, B>{ ... } let x = f()?; // 以下と同じ // let x = match f() { // Ok(x) => x, // Err(x) => return Err(x) // };Debug
構造体に
Debug属性を付加することでデフォルトのfmt()を実装することができる。カスタムの文字列化を実装する場合はimplを使用する。#[derive(Debug)] struct Foo { f1: u32, f2: f64, } println!("{:?}", Foo { f1: 1, f2: 0.5 }); // Foo { f1: 1, f2: 0.5 }use std::fmt; struct Bar { b1: u32, b2: f64 } impl fmt::Debug for Bar { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Bar [ x = {}, y = {} ]", self.b1, self.b2) } } println!("{:?}", Bar { b1: 1, b2: 0.5 }); // Bar [ x = 1, y = 0.5 ]