kdnakt blog

hello there.

Rust dojo第43回を開催した

第43回です。前回はこちら。

kdnakt.hatenablog.com

 

 

[第43回の様子]

2022/06/15に第43回を開催した。

 

内容としてはRust By Example 日本語版の「15.3. 借用」、「15.3.1. ミュータビリティ」、「15.3.2. エイリアス」に取り組んだ。

 

参加者は自分を入れて4人。ちょっと減った😢

今回は久しぶりにドライバを担当した。

 

[学んだこと]

  • 15.3. 借用
  • 前回は関数の引数をT型にして、完全に所有権を渡すやり方を学んだ
  • 今回は所有権を持ちつつ、一時的に借用させるために、&T型を利用する
// この関数はi32を借用する
fn borrow_i32(borrowed_i32: &i32) {
    println!("This int is: {}", borrowed_i32);
}

fn main() {
    // ボックス化された整数
    let boxed_i32 = Box::new(5_i32);
    // スタック化された整数
    let stacked_i32 = 6_i32;

    // 借用
    borrow_i32(&boxed_i32);
    borrow_i32(&stacked_i32);

    // 再利用が可能
    borrow_i32(&boxed_i32);
    borrow_i32(&stacked_i32);
}
  • Rustのコンパイラが借用をチェックし、破棄できないようにしてくれている
fn eat_box_i32(boxed_i32: Box<i32>) {
    println!("Destroying box that contains {}", boxed_i32);
}

fn borrow_i32(borrowed_i32: &i32) {
    println!("This int is: {}", borrowed_i32);
}

fn main() {
    let boxed_i32 = Box::new(5_i32);

    {
        // 参照を取得
        let _ref_to_i32: &i32 = &boxed_i32;

        eat_box_i32(boxed_i32); // コンパイルエラー:直後で利用されているため破棄できない

    // 参照を利用
        borrow_i32(_ref_to_i32);
    }
}

// コンパイルエラーは以下の通り
error[E0505]: cannot move out of `boxed_i32` because it is borrowed
  --> src/main.rs:36:21
   |
29 |         let _ref_to_i32: &i32 = &boxed_i32;
   |                                 ---------- borrow of `boxed_i32` occurs here
...
36 |         eat_box_i32(boxed_i32);
   |                     ^^^^^^^^^ move out of `boxed_i32` occurs here
...
41 |         borrow_i32(_ref_to_i32);
   |                    ----------- borrow later used here

#[derive(Clone, Copy)]
struct Book {
    // `&'static str`はread-onlyメモリ上の文字列への参照
    author: &'static str,
    title: &'static str,
    year: u32,
}

fn borrow_book(book: &Book) {
    println!("I immutably borrowed {} - {} edition", book.title, book.year);
}

fn new_edition(book: &mut Book) {
    book.year = 2014;
    println!("I mutably borrowed {} - {} edition", book.title, book.year);
}

fn main() {
    // イミュータブルなBookを作成
    let immutabook = Book {
        author: "Douglas Hofstadter",
        title: "Gödel, Escher, Bach",
        year: 1979,
    };

    // ミュータブルなコピーを作成
    let mut mutabook = immutabook;

    borrow_book(&mutabook);
    // I immutably borrowed Gödel, Escher, Bach - 1979 edition
    
    // ミュータブルなオブジェクトをミュータブルに借用する
    new_edition(&mut mutabook);
    // I mutably borrowed Gödel, Escher, Bach - 2014 edition
    
    // 再利用:中身が変わっている
    borrow_book(&mutabook);
    // I immutably borrowed Gödel, Escher, Bach - 2014 edition
}
  • ミュータブルでない変数に書き込もうとするとコンパイルエラーになる
fn borrow_book(book: &Book) {
    book.year = 2014; // コンパイルエラー!
    println!("I immutably borrowed {} - {} edition", book.title, book.year);
}

// エラー内容は以下の通り
error[E0594]: cannot assign to `book.year`, which is behind a `&` reference
  --> src/main.rs:14:5
   |
13 | fn borrow_book(book: &Book) {
   |                      ----- help: consider changing this to be a mutable reference: `&mut Book`
14 |     book.year = 2014;
   |     ^^^^^^^^^^^^^^^^ `book` is a `&` reference, so the data it refers to cannot be written
  • 15.3.2. エイリアス
  • ミュータブルな借用には条件がある
    • イミュータブルな借用と同時に行うことはできない
    • 同時にミュータブルに借用できるのは1つだけ
  • イミュータブルな借用が既に存在する場合、ミュータブルに借用しようとするとコンパイルエラーとなる
struct Point { x: i32, y: i32, z: i32 }

fn main() {
    let mut point = Point { x: 0, y: 0, z: 0 };

    let borrowed_point = &point;
    let another_borrow = &point;

    // イミュータブルな借用は複数可能
    println!("Point has coordinates: ({}, {}, {})",
                borrowed_point.x, another_borrow.y, point.z);

    // イミュータブルな借用が生きているため、ミュータブルに借用できない
    let mutable_borrow = &mut point; // コンパイルエラー

    // イミュータブルな借用はここまで生きている
    println!("Point has coordinates: ({}, {}, {})",
                borrowed_point.x, another_borrow.y, point.z);
}

// 以下のコンパイルエラー
error[E0502]: cannot borrow `point` as mutable because it is also borrowed as immutable
  --> src/main.rs:18:26
   |
6  |     let borrowed_point = &point;
   |                          ------ immutable borrow occurs here
...
18 |     let mutable_borrow = &mut point;
   |                          ^^^^^^^^^^ mutable borrow occurs here
...
23 |                 borrowed_point.x, another_borrow.y, point.z);
   |                 ---------------- immutable borrow later used here
  • 反対に、ミュータブルな借用が存在すると、イミュータブルに借用できない
struct Point { x: i32, y: i32, z: i32 }

fn main() {
    let mut point = Point { x: 0, y: 0, z: 0 };

    // ミュータブルに借用しデータを書き換える
    let mutable_borrow = &mut point;
    mutable_borrow.x = 5;
    mutable_borrow.y = 2;
    mutable_borrow.z = 1;

    // ミュータブルな借用が生きているためイミュータブルに借用できない
    let y = &point.y; // コンパイルエラー
    
    // ミュータブルな借用はここまで生きている
    println!("Point has coordinates: ({}, {}, {})",
                mutable_borrow.x, mutable_borrow.y, mutable_borrow.z);
}

// 以下のコンパイルエラー
error[E0502]: cannot borrow `point.y` as immutable because it is also borrowed as mutable
  --> src/main.rs:39:13
   |
27 |     let mutable_borrow = &mut point;
   |                          ---------- mutable borrow occurs here
...
39 |     let y = &point.y;
   |             ^^^^^^^^ immutable borrow occurs here
...
51 |                 mutable_borrow.x, mutable_borrow.y, mutable_borrow.z);
   |                 ---------------- mutable borrow later used here
  • 変則的な例としては、println!もイミュータブルなリファレンスを取るため、ミュータブルな借用と両立しない
struct Point { x: i32, y: i32, z: i32 }

fn main() {
    let mut point = Point { x: 0, y: 0, z: 0 };

    let mutable_borrow = &mut point;
    mutable_borrow.x = 5;
    mutable_borrow.y = 2;
    mutable_borrow.z = 1;

    // ミュータブルに借用されている状態でイミュータブルなリファレンスを取れない
    println!("Point Z coordinate is {}", point.z); // コンパイルエラー

    println!("Point has coordinates: ({}, {}, {})",
                mutable_borrow.x, mutable_borrow.y, mutable_borrow.z);
}

// 以下のコンパイルエラー
error[E0502]: cannot borrow `point.z` as immutable because it is also borrowed as mutable
  --> src/main.rs:45:42
   |
27 |     let mutable_borrow = &mut point;
   |                          ---------- mutable borrow occurs here
...
45 |     println!("Point Z coordinate is {}", point.z);
   |                                          ^^^^^^^ immutable borrow occurs here
...
51 |                 mutable_borrow.x, mutable_borrow.y, mutable_borrow.z);
   |                 ---------------- mutable borrow later used here
   |
   = note: this error originates in the macro `$crate::format_args_nl` (in Nightly builds, run with -Z macro-backtrace for more info)

 

[まとめ]

モブプログラミングスタイルでRust dojoを開催した。

println!マクロはイミュータブルな借用だったのか...ちょっと賢くなれたかな。

 

プルリクエストはこちら。

github.com