前の記事
- 【0】 準備 ← 初回
- ...
- 【6】 カプセル化の続きと所有権とセッター ~そして不変参照と可変参照!~ ← 前回
- 【7】 スタック・ヒープと参照のサイズ ~ メモリの話 ~ ← 今回
全記事一覧
- 【0】 準備
- 【1】 構文・整数・変数
- 【2】 if・パニック・演習
- 【3】 可変・ループ・オーバーフロー
- 【4】 キャスト・構造体 (たまにUFCS)
- 【5】 バリデーション・モジュールの公開範囲 ~ → カプセル化!~
- 【6】 カプセル化の続きと所有権とセッター ~そして不変参照と可変参照!~
- 【7】 スタック・ヒープと参照のサイズ ~メモリの話~
- 【8】 デストラクタ(変数の終わり)・トレイト ~終わりと始まり~
- 【9】 Orphan rule (孤児ルール)・演算子オーバーロード・derive ~Empowerment 💪 ~
- 【10】 トレイト境界・文字列・Derefトレイト ~トレイトのアレコレ~
- 【11】 Sized トレイト・From トレイト・関連型 ~おもしろトレイトと関連型~
- 【12】 Clone・Copy・Dropトレイト ~覚えるべき主要トレイトたち~
- 【13】 トレイトまとめ・列挙型・match式 ~最強のトレイトの次は、最強の列挙型~
- 【14】 フィールド付き列挙型とOption型 ~チョクワガタ~
- 【15】 Result型 ~Rust流エラーハンドリング術~
- 【16】 Errorトレイトと外部クレート ~依存はCargo.tomlに全部お任せ!~
- 【17】 thiserror・TryFrom ~トレイトもResultも自由自在!~
- 【18】 Errorのネスト・慣例的な書き方 ~Rustらしさの目醒め~
- 【19】 配列・動的配列 ~スタックが使われる配列と、ヒープに保存できる動的配列~
- 【20】 動的配列のリサイズ・イテレータ ~またまたトレイト登場!~
- 【21】 イテレータ・ライフタイム ~ライフタイム注釈ようやく登場!~
- 【22】 コンビネータ・RPIT ~ 「
Iterator
トレイトを実装してるやつ」~ - 【23】
impl Trait
・スライス ~配列の欠片~ - 【24】 可変スライス・下書き構造体 ~構造体で状態表現~
- 【25】 インデックス・可変インデックス ~インデックスもトレイト!~
- 【26】 HashMap・順序・BTreeMap ~Rustの辞書型~
- 【27】 スレッド・'staticライフタイム ~並列処理に見るRustの恩恵~
- 【28】 リーク・スコープ付きスレッド ~ライフタイムに技あり!~
- 【29】 チャネル・参照の内部可変性 ~Rustの虎の子、mpscと
Rc<RefCell<T>>
~ - 【30】 双方向通信・リファクタリング ~返信用封筒を入れよう!~
- 【31】 上限付きチャネル・PATCH機能 ~パンクしないように制御!~
- 【32】
Send
・排他的ロック(Mutex
)・非対称排他的ロック(RwLock
) ~真打Arc<Mutex<T>>
登場~ - 【33】 チャネルなしで実装・Syncの話 ~考察回です~
- 【34】
async fn
・非同期タスク生成 ~Rustの非同期入門~ - 【35】 非同期ランタイム・Futureトレイト ~非同期のお作法~
- 【36】 ブロッキング・非同期用の実装・キャンセル ~ラストスパート!~
- 【37】 Axumでクラサバ! ~最終回~
- 【おまけ1】 Rustで勘違いしていたこと3選 🏄🌴 【100 Exercises To Learn Rust 🦀 完走記事 🏃】
- 【おまけ2】 【🙇 懺悔 🙇】Qiitanグッズ欲しさに1日に33記事投稿した話またはQiita CLIとcargo scriptを布教する的な何か
100 Exercise To Learn Rust 演習第7回になります!
今回の関連ページ
[03_ticket_v1/08_stack] スタック
問題はこちらです。
// TODO: based on what you learned in this section, replace `todo!()` with
// the correct **stack size** for the respective type.
#[cfg(test)]
mod tests {
use std::mem::size_of;
#[test]
fn u16_size() {
assert_eq!(size_of::<u16>(), todo!());
}
#[test]
fn i32_size() {
assert_eq!(size_of::<i32>(), todo!());
}
#[test]
fn bool_size() {
assert_eq!(size_of::<bool>(), todo!());
}
}
今回は各型がメモリ上で専有するサイズ当てゲームですね...!
解説
#[cfg(test)]
mod tests {
use std::mem::size_of;
#[test]
fn u16_size() {
// 16 / 8 = 2
assert_eq!(size_of::<u16>(), 2);
}
#[test]
fn i32_size() {
// 32 / 8 = 4
assert_eq!(size_of::<i32>(), 4);
}
#[test]
fn bool_size() {
// 1バイトあれば足りそう
assert_eq!(size_of::<bool>(), 1);
}
}
16ビットなので2バイト、32ビットなので4バイト、1ビットあれば足りそうだけどマシンの利便性を考慮して1バイト、と回答して実際に当たっています。
スタックの話も是非したいところですが、一般的なコンサイの資料やBookに譲りたいと思います。また第12回で Clone
トレイトと Copy
トレイトの話をするのですが、 Copy
トレイト実装可能要件の一つが「データがスタック上にあること」なので第12回で触れ直す方が実用的な話題だと思うので飛ばします(ん?今全部話してしまったような...?)
[03_ticket_v1/09_heap] ヒープ
問題はこちらです。
pub struct Ticket {
title: String,
description: String,
status: String,
}
// TODO: based on what you learned in this section, replace `todo!()` with
// the correct **stack size** for the respective type.
#[cfg(test)]
mod tests {
use super::Ticket;
use std::mem::size_of;
#[test]
fn string_size() {
assert_eq!(size_of::<String>(), todo!());
}
#[test]
fn ticket_size() {
// This is a tricky question!
// The "intuitive" answer happens to be the correct answer this time,
// but, in general, the memory layout of structs is a more complex topic.
// If you're curious, check out the "Data layout" section of the Rustonomicon
// https://doc.rust-lang.org/nomicon/data.html for more information.
assert_eq!(size_of::<Ticket>(), todo!());
}
}
解説
pub struct Ticket {
title: String,
description: String,
status: String,
}
#[cfg(test)]
mod tests {
use super::Ticket;
use std::mem::size_of;
#[test]
fn string_size() {
// 64bit = 8 byte OS, so usize's size is 8 byte and Strings's one is 8 * 3 = 24
assert_eq!(size_of::<String>(), 24);
}
#[test]
fn ticket_size() {
// intuitive: 24 * 3 = 72
assert_eq!(size_of::<Ticket>(), 72);
}
}
ヒープの話もめっちゃしたいですね...! Box
型とか Vec
型とか String
型とか Rc
型とかはヒープにもメモリがアロケートされます。ヒープに確保されるメモリ量自体は実行時までわからないですが、 ヒープへの参照はスタックに積まれるしサイズがわかる (なので、 Box
それ自体は Sized
だけど、 Box<T>
の T
は ?Sized
、つまりサイズ不定でも良い)ということは覚えておくと便利かなって思います。
Vec
や String
というのはつまり内部にヒープへの参照を持っている型です。
String
型は、以下3つのフィールドを持っています
- ヒープへの参照 ==
usize
型の値 -> 64ビットOSなら8バイト - 長さ (length) == 同じく
usize
型の値 - 最大長 == 同じく
usize
型の値
以上より、String
型は24バイトになります。
そして直感的に、 String
型のフィールドを3つ持つので、 Title
型のスタック上で専有するメモリサイズは72バイトとなります。
直感が当たる場合と当たらない場合があるということですが、これはRustの構造体のメモリ上での確保スペースは最適化されたりされなかったりするという性質のことを指してそうです。
筆者もこの辺の詳細は良くわかっていませんが、とりあえず一つ知っているのはC言語とは異なるということです。FFIのためにC言語の構造体メモリレイアウトに合わせる命令があるので、参考としてリンクを載せておきます。
- 参考: 代替メモリレイアウト
[03_ticket_v1/10_references_in_memory] 参照のメモリサイズ
問題はこちらです。
pub struct Ticket {
title: String,
description: String,
status: String,
}
// TODO: based on what you learned in this section, replace `todo!()` with
// the correct **stack size** for the respective type.
#[cfg(test)]
mod tests {
use super::Ticket;
use std::mem::size_of;
#[test]
fn u16_ref_size() {
assert_eq!(size_of::<&u16>(), todo!());
}
#[test]
fn u64_mut_ref_size() {
assert_eq!(size_of::<&mut u64>(), todo!());
}
#[test]
fn ticket_ref_size() {
assert_eq!(size_of::<&Ticket>(), todo!());
}
}
またまたサイズ当て問題です!
解説
pub struct Ticket {
title: String,
description: String,
status: String,
}
#[cfg(test)]
mod tests {
use super::Ticket;
use std::mem::size_of;
#[test]
fn u16_ref_size() {
assert_eq!(size_of::<&u16>(), size_of::<usize>()); // == 8
}
#[test]
fn u64_mut_ref_size() {
assert_eq!(size_of::<&mut u64>(), size_of::<usize>());
}
#[test]
fn ticket_ref_size() {
assert_eq!(size_of::<&Ticket>(), size_of::<usize>());
}
}
(通常の)参照のメモリサイズは usize
と同じなので、 size_of::<usize>()
が正解になります。ちなみに 64bit OSはポインタサイズが64ビットなので usize
は8バイトを専有します。
では次の問題に行きましょう!