第43回です。前回はこちら。
[第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
- 15.3.1. ミュータビリティ
- 借用はミュータブルに行うことができる
#[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!マクロはイミュータブルな借用だったのか...ちょっと賢くなれたかな。
プルリクエストはこちら。