🔥
[Rust]今日の学び2022-08-16 - ボックス化はどのような場合に行うのか(初歩的意訳と練習)
これは個人的な練習のノートです。
間違っていた場合はごめんなさい。
御手柔らかにお願いします。アドバイスを貰えると喜びます。
はじめに
今年中に、手足のようにRustを使える自信をつけたくて
Effective Rustを1章ずつ読んでいく目標を立てました。
ただ、それだけだと飽きてしまう恐れがある。
不安な基本部分を取り出して咀嚼したり練習をしています。
ここでは、その備忘録を公開しようと思いますしようと思います。
今日はボックス化について
- ボックス化はどのような場合に行うのか
- リファレンスのユースケース(英文)を意訳する
- 練習: 整数型を使ったボックス化
- 簡単なコードを書きながら理解する
をやっていきます。
参考情報
- web.mit.edu - The Rust Programming Language(First Edition) - Effective Rust
- doc.rust-jp.rs - rust-by-example-ja/scope/borrow - 借用
- doc.rust-lang.org/book/ch15-01-box.html - Using Box<T> to Point to Data on the Heap
本題
ボックス化はどのような場合に行うのか
-
Rust by example 日本語版のBox, スタックとヒープを読んだ
- ボックス化は、いわゆるスマートポインタ。ボックス化することで意図的にヒープ上に割り当てることができるということは理解。
-
どのようなケースでボックス化を意識して行うべき(あるいは行わないべき)かを知りたい
- 基本的には、ヒープ上の領域確保は、スタックと比べると重たい処理。ボックス化(要するに、ヒープ確保)をすると、スタックの高速な領域確保の利点が失われることになる。
- 意図的にヒープ上の領域確保を考えるべきケースを除いて、やらないほうがよさそうという認識している。
- その上で、どのようなケースでボックス化を意識して行うべき(あるいは行わないべき)かを知りたい。
- 基本的には、ヒープ上の領域確保は、スタックと比べると重たい処理。ボックス化(要するに、ヒープ確保)をすると、スタックの高速な領域確保の利点が失われることになる。
-
doc.rust-lang.org(英語版)の説明があった。愚直に翻訳することにした。
-
doc.rust-lang.org/book/ch15-01-box.html - Using Box<T> to Point to Data on the Heap
- 3つのユースケースが紹介されていた。抜粋しつつ個人的に意訳した。
- Case1. When you have a large amount of data and you want to transfer ownership but ensure the data won’t be copied when you do so
- 個人的意訳: 大規模データを扱っており、かつ、所有権の移動が発生するようなソースコードで、データのコピーが発生しないことを確実に保証する目的でボックス化を行う
- Case2. When you have a type whose size can’t be known at compile time and you want to use a value of that type in a context that requires an exact size
- 個人的意訳: コンパイル時にサイズがわからない型を扱う場合(かつ、その型の値の正確なサイズが必要なケース)
- Case3. When you want to own a value and you care only that it’s a type that implements a particular trait rather than being of a specific type
- 個人的意訳: 自分で値の所有権を保持したい場合で、かつ、その値の具体的な型よりもその値の型がなんらかのtrait(性質)が実装したものであることの方を重視したい場合
- Case1. When you have a large amount of data and you want to transfer ownership but ensure the data won’t be copied when you do so
- 感想
- Case1, Case2はなんとなくわかる。Case3はどの程度発生するケースなのか疑問が残ったままだ(実践が必要)
- 自分で値の所有権を保持したい場合はわからないが、traitで抽象化して扱うことはそれなりにありそう(意外とよく使うのか?)
- 参考にできるコードを探して読んでみようと思う。
- 自分で値の所有権を保持したい場合はわからないが、traitで抽象化して扱うことはそれなりにありそう(意外とよく使うのか?)
- Case1, Case2はなんとなくわかる。Case3はどの程度発生するケースなのか疑問が残ったままだ(実践が必要)
- 3つのユースケースが紹介されていた。抜粋しつつ個人的に意訳した。
-
doc.rust-lang.org/book/ch15-01-box.html - Using Box<T> to Point to Data on the Heap
練習: 整数型を使ったボックス化
- このようなコードを実際に書くことはないということは理解の上で練習。
- Playground上で練習しました
- Box<T>した後の参照・借用、その後の値の書き換え。エラーが発生するポイントを、教科書的に確認していく作業。
- ボックス化した後を参照してから中身を更新するコードを書くと、C言語の2重ポインタに似た感じになるな。
fn awesome_borrow_with_box(int_on_box_borrow: &Box<i32>) -> bool {
// 以下のコードはコメントアウトするとエラーになる 理由: expected struct `Box`, found integer
// *int_on_box_borrow = 101;
// 以下のコードはコメントアウトするとエラーになる 理由: cannot assign to `**int_on_box_borrow`, which is behind a `&` reference
// **int_on_box_borrow = 101;
println!("int with boxed: borrow = {}", int_on_box_borrow);
true
}
fn main() {
// 通常のinteger(スタック上に格納される)
let int_on_stack: i32 = 100;
// 変数int_on_stack_copyにコピー ※値の変更確認のためmut修飾子を付与)
let mut int_on_stack_copy = int_on_stack;
println!("int on stack: copy is ok. original = {}, copy = {}.", int_on_stack, int_on_stack_copy);
println!("int on stack: change value of int_on_stack_copy(from 100 to 50).");
//
int_on_stack_copy = 50;
println!("int on stack: copy is stored as different on stack. original = {}, copy = {}.", int_on_stack, int_on_stack_copy);
let mut int_on_box: Box<i32> = std::boxed::Box::new(100);
// box化した値の参照・借用の動作を確認
let int_on_box_borrow_local = &int_on_box;
// 以下のコードはコメントアウトするとエラーになる 理由: cannot assign to `*int_on_box` because it is borrowed (L29の参照で値が借用されている→元の変数int_on_boxに新しい値を代入できない)
// *int_on_box = 102;
// 以下のコードはコメントアウトするとエラーになる 理由: expected struct `Box`, found integer(int_on_box_borrow_localは、i32をbox化した値の参照だから、*int_on_box_borrow_localと書くとこれは struct Box型が返される)
// *int_on_box_borrow_local = 102;
// 以下のコードはコメントアウトするとエラーになる 理由: cannot assign to `**int_on_box_borrow_local`, which is behind a `&` reference
// **int_on_box_borrow_local = 102;
println!("int with boxed: int_on_box_borrow_local={}, original = {}", int_on_box_borrow_local, int_on_box);
// 関数の呼び出しを通じて値の借用を試す
awesome_borrow_with_box(&int_on_box);
// L43の関数実行が終了し借用値の→元の変数int_on_boxに新しい値を代入できない)
*int_on_box = 103;
println!("int with boxed: original = {}", int_on_box);
let int_on_box_borrow_mut = &mut int_on_box;
**int_on_box_borrow_mut = 104;
// 以下のコードはコメントアウトするとエラーになる 理由: error[E0502]: cannot borrow `int_on_box` as immutable because it is also borrowed as mutable
// println!("int with boxed: int_on_box_borrow_mut={}, original = {}", int_on_box_borrow_mut, int_on_box);
// 以下のコードはコメントアウトするとエラーになる 理由: error[E0499]: cannot borrow `int_on_box` as mutable more than once at a time
// println!("int with boxed: int_on_box_borrow_mut={}, original = {}", int_on_box_borrow_mut, &mut int_on_box);
println!("int with boxed: int_on_box={}", int_on_box);
}
- 実行結果
int on stack: copy is ok. original = 100, copy = 100.
int on stack: change value of int_on_stack_copy(from 100 to 50).
int on stack: copy is stored as different on stack. original = 100, copy = 50.
int with boxed: int_on_box_borrow_local=100, original = 100
int with boxed: borrow = 100
int with boxed: original = 103
int with boxed: int_on_box=104
おわりに
所有権が難しい。
借りられてる変数を一瞬忘れてprintln!しようとすると不意にエラーになって、借用、むずかしい。。という気持ちになった。
Rustの満足行く開発ができるようになるまで
ちょっとずつやっていきます。
Discussion