Chapter 02

基本

📌 Hello World!!

以下は,おなじみの Hello World プログラムです.非常に単純で直感的に書けます.Rust Playground というサイトでは Rust プログラムを手軽に試すことができます.試しに下の内容を実行してみても構いません.サイトを開いたら最初から似たようなプログラムが入力されているかもしれません.

fn main() {
    print!("hello world!")
}

📌 コメント

Rust のコメントには,一行コメント ( // )と,ブロックコメント( /* )(*/)があります. /* ブロックコメントは /* このようにネストして */ 書くことができます.*/

📌 式と文

Rust は式ベースの言語です.ほとんどが (expression)で表されます.ここで式は返り値を評価するものです.簡単に言うと,式は何かしらの値を返します.それに対して, (statement)は処理を実行しますが値を返しません.

📌 ブロック

式の1つに ブロック があります.これは {} で囲んだものです.例えば {0} というのは 0 を返す式です.ブロックが返す値は省略することができます.その場合は () を返します.この ()ユニット と言います.つまり,{} というのは {()} ということになります.
ブロックには2つの機能があります.1つはスコープを作成します.もう1つは,文をいくつも記述できることです.

{ statement; statement; statement; ...; (expression) }

ここでセミコロン (;) は式を文に変化させるものです.

📌 オブジェクト

数値や関数や参照など,型の実体はすべて オブジェクト です.つまり,式が返す値もまたオブジェクトになります.例えば, 1 という値も数値オブジェクトであり,1 == {1} という関係にあります.

📌 所有権

オブジェクトには 所有権 (Ownership) が付いています.この所有権には2つの属性があります.

rust-onwership01

📌 束縛

let 文を使うことでオブジェクトと変数を 束縛 します.変数はそのスコープから外れたときに束縛していた所有権を放棄します.また,最初に束縛したオブジェクトの所有権は基本的に原本となり,原本および仮の所有権がすべて放棄された時にオブジェクトは破棄されます.

onwership02

📌 参照

Rust では所有権を使ってオブジェクトを受け渡します.通常は所有権を渡してしまうと束縛が解除されて,受け取った側がそれを束縛します.そこで,仮の所有権を作成して相手に渡すことで,渡す側は束縛を解除されず,仮の所有権を受け取った側はその所有権を使ってオブジェクトを操作することが出来ます.そして,受け取った側の変数がスコープを外れた時に束縛していた仮の所有権が破棄されます.この時,原本または他の仮の所有権があればオブジェクトは破棄されません.仮の所有権を作成する方法の1つが 参照 (reference)です.これは & 演算子を使います.

📌 可変性

Rust は標準でオブジェクトを 不変 (immutable)で束縛します.そこで, let ではなく let mut を使うことで,オブジェクトを 可変 (mutable)で束縛することが出来ます.
今までのことをまとめると次のようになります.

rust-ownership03

原本の所有権から仮の所有権を作成することが出来ます.また,仮の所有権を複製することも出来ますし,仮の所有権からさらに仮の所有権を作れます.ただし,仮の所有権から原本の所有権は作れません.

rust-onwership04

📌 借用チェック

同一オブジェクトに対する参照と可変について,いくつか制限があります.

  • 不変参照 (&) は何個でも同時に存在することが出来る
  • 不変参照 (&) と可変参照 (&mut) は同時に存在することが出来ない
  • 可変参照 (&mut) は同時に1つしか存在することが出来ない

ここで大事なことは,上記の制限は 関数呼び出し時 (かつコンパイル時)にチェックされるということです(これを 借用チェック と呼びます).このチェックが行われる直前の可変参照(必ず1つ)もしくは不変参照(複数可)がその時に存在していることになります.少なくとも可変参照を作成した時には,それまでの不変参照または可変参照がすべて無効となり,存在しないことになります.もちろん,あくまで同一オブジェクトに対する参照に対してです.
以下は複数の不変参照が存在しても問題のないコードです.

fn main() {
    let a = 10;               // immutable object
    let aref1 = &a;           // reference
    let aref2 = &a;           // reference
    println!("{}, {}, {}", a, aref1, aref2); // borrow check!! - OK
}

次は,可変参照をしているところが複数ありますが,借用チェック時に制約を満たしているのでこれも問題ありません.

fn main() {
    let mut a = 10;           // mutable object
    let a_ref1 = &a;          // reference
    let a_mut_ref1 = &mut a;  // mutable reference
    let a_mut_ref2 = &mut a;  // mutable refernece
    *a_mut_ref2 = 20;         // assign
    println!("{}", a);        // borrow check!! - OK
}

次は,可変参照を複数存在してしまうことになるのでコンパイルエラーになります.

fn main() {
    let mut a = 10;           // mutable object
    let a_ref1 = &a;          // reference
    let a_mut_ref1 = &mut a;  // mutable reference
    let a_mut_ref2 = &mut a;  // この時点で a_ref1, a_mut_ref1 は存在しない
    *a_mut_ref1 = 20;         // assign (error)
    println!("{}", a);        // borrow check!! - Error!
}

次は,可変参照と不変参照が同時に存在してしまうことになるのでコンパイルエラーになります.

fn main() {
    let mut a = 10;           // mutable object
    let a_ref1 = &a;          // reference
    let a_mut_ref1 = &mut a;  // mutable reference
    let a_mut_ref2 = &mut a;  // この時点で a_ref1, a_mut_ref1 は存在しない
    println!("{}", a_ref1);   // borrow check!! - Error!
}

不変参照や可変参照を複数束縛しているコードでも借用チェック時に制約を満たしていればコンパイルは通ります.

fn main() {
    let mut a = 10;           // mutable object
    let a_ref1 = &a;          // reference
    let a_mut_ref1 = &mut a;  // mutable reference
    let a_mut_ref2 = &mut a;  // この時点で a_ref1, a_mut_ref1 は存在しない
    let a_ref2 = &a;          // この時点で a_mut_ref2 は存在しない
    //println!("{}", a_mut_ref2);        // borrow check!! - Error!
    //println!("{} {}", a_ref1, a_ref2); // borrow check!! - Error!
    println!("{}", a_ref2);   // borrow check!! - OK
}

このように関数呼び出しによる借用チェックによって,スコープから抜けていない変数であっても,それが参照なら存在していないことになりうるということです(ここで存在していないと言っていますが,実際には存在できないようにコンパイル時にエラーが出るということ).参照を束縛した変数をなるべく作らないことが大切です.

📌 参照外し

参照(仮の所有権)を使ってオブジェクトの操作をする場合は 参照外し(dereference)が必要です.これは * 演算子を使います.

fn main() {
    let mut a = 10;            // mutable object
    let a_mut_ref = &mut a;    // mutable reference
    *a_mut_ref = 20;           // dereference and assign
    println!("{}", a_mut_ref); // auto dereference
}

参照外しは関数に渡した時や, . 演算子によるフィールド操作・メソッド呼び出し時などにおいて自動で行われる場合があります.