第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!マクロはイミュータブルな借用だったのか...ちょっと賢くなれたかな。
プルリクエストはこちら。