1
0
お題は不問!Qiita Engineer Festa 2024で記事投稿!
Qiita Engineer Festa20242024年7月17日まで開催中!

Rust 100 Ex 🏃【7/37】 スタック・ヒープと参照のサイズ ~メモリの話~

Last updated at Posted at 2024-07-17

前の記事

全記事一覧

100 Exercise To Learn Rust 演習第7回になります!

今回の関連ページ

[03_ticket_v1/08_stack] スタック

問題はこちらです。

lib.rs
// 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!());
    }
}

今回は各型がメモリ上で専有するサイズ当てゲームですね...!

解説

lib.rs
#[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] ヒープ

問題はこちらです。

lib.rs
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!());
    }
}

解説

lib.rs
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 、つまりサイズ不定でも良い)ということは覚えておくと便利かなって思います。

VecString というのはつまり内部にヒープへの参照を持っている型です。

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] 参照のメモリサイズ

問題はこちらです。

lib.rs
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!());
    }
}

またまたサイズ当て問題です!

解説

lib.rs
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バイトを専有します。

では次の問題に行きましょう!

次の記事: 【8】 デストラクタ(変数の終わり)・トレイト ~終わりと始まり~

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0