Std::mem: sizes
cargo run --bin 52_memsize_of::<T>() is the size of a type in bytes, and align_of::<T>() its alignment. Both are in the prelude. size_of_val looks at a value, which matters for slices and other unsized data. Rust may reorder struct fields to reduce padding.
The three functions for moving values out of a &mut are the most useful part of the module. swap(a, b) exchanges two values. replace(dest, new) puts a new value in and hands back the old one. take(dest) is replace with Default::default(), leaving an empty Vec or String behind. They let you move out of a struct field you only borrow.
drop(v) ends a value early. forget(v) never runs its destructor, leaking it; that is safe but rarely what you want. discriminant tells two enum values' variants apart without comparing their data.
Sizes and alignment
fn demo_sizes() {
println!("-- sizes --");
println!(
"u8 {} i32 {} u64 {} u128 {} char {} bool {}",
size_of::<u8>(),
size_of::<i32>(),
size_of::<u64>(),
size_of::<u128>(),
size_of::<char>(),
size_of::<bool>()
);
println!(
"&u8 {} &[u8] {} &str {} Box<u8> {}",
size_of::<&u8>(),
size_of::<&[u8]>(),
size_of::<&str>(),
size_of::<Box<u8>>()
);
println!(
"String {} Vec<u64> {} Option<String> {}",
size_of::<String>(),
size_of::<Vec<u64>>(),
size_of::<Option<String>>()
);
println!(
"() {} [u32; 4] {} (u8, u32) {}",
size_of::<()>(),
size_of::<[u32; 4]>(),
size_of::<(u8, u32)>()
);
println!(
"align u8 {} u32 {} u64 {}",
align_of::<u8>(),
align_of::<u32>(),
align_of::<u64>()
);
}Field reordering and size_of_val
#[allow(dead_code)]
struct Padded {
a: u8,
b: u64,
c: u8,
}
fn demo_layout() {
println!("-- layout --");
// In declaration order this would be 24 bytes; Rust reorders to 16.
println!("Padded {} bytes (fields u8, u64, u8)", size_of::<Padded>());
let v = vec![1u32; 10];
println!(
"size_of_val(&v) {} (the Vec header) slice {}",
size_of_val(&v),
size_of_val(&v[..])
);
println!("size_of_val(\"hello\") {}", size_of_val("hello"));
}Swap and replace
fn demo_swap_replace() {
println!("-- swap / replace --");
let mut a = 1;
let mut b = 2;
mem::swap(&mut a, &mut b);
println!("{a} {b}");
let mut v = vec![String::from("x"), String::from("y")];
v.swap(0, 1); // for elements of one slice, use the slice method
println!("{v:?}");
let old = mem::replace(&mut a, 9);
println!("old {old} now {a}");
}Take and replace for moving out of a borrowed struct
#[derive(Debug, Default)]
struct Buffer {
lines: Vec<String>,
title: String,
}
impl Buffer {
// Hand the lines to the caller and leave an empty Vec behind.
fn flush(&mut self) -> Vec<String> {
mem::take(&mut self.lines)
// self.lines // error[E0507]: cannot move out of `self.lines` which is behind a mutable reference
}
fn rename(&mut self, new: &str) -> String {
mem::replace(&mut self.title, new.to_string())
}
}
fn demo_take() {
println!("-- take --");
let mut v = vec![1, 2, 3];
let taken = mem::take(&mut v);
println!("{taken:?} left {v:?}");
let mut buf = Buffer { lines: vec!["a".into(), "b".into()], title: "draft".into() };
let flushed = buf.flush();
let old_title = buf.rename("final");
println!("flushed {flushed:?} old title {old_title} now {buf:?}");
let mut opt = Some(5);
println!("Option::take {:?} left {opt:?}", opt.take());
}A state machine that moves out of self with replace
enum State {
Idle,
Running { job: String },
Done { job: String, ok: bool },
}
impl State {
fn advance(&mut self) {
*self = match mem::replace(self, State::Idle) {
State::Idle => State::Running { job: "build".into() },
State::Running { job } => State::Done { job, ok: true },
done @ State::Done { .. } => done,
};
}
fn label(&self) -> String {
match self {
State::Idle => "idle".into(),
State::Running { job } => format!("running {job}"),
State::Done { job, ok } => format!("done {job} ok={ok}"),
}
}
}
fn demo_state_replace() {
println!("-- replace in a state machine --");
let mut s = State::Idle;
for _ in 0..3 {
s.advance();
println!("{}", s.label());
}
}Drop, forget, and needs_drop
struct Loud(&'static str);
impl Drop for Loud {
fn drop(&mut self) {
println!(" drop {}", self.0);
}
}
fn demo_drop_forget() {
println!("-- drop / forget --");
let a = Loud("a");
let b = Loud("b");
drop(a);
mem::forget(b); // b's destructor never runs
println!(
" needs_drop String {} i32 {}",
mem::needs_drop::<String>(),
mem::needs_drop::<i32>()
);
}Comparing enum variants with discriminant
#[derive(Debug)]
#[allow(dead_code)] // the fields are only here to show that discriminant ignores them
enum Event {
Click { x: i32, y: i32 },
Key(char),
}
fn demo_discriminant() {
println!("-- discriminant --");
let a = Event::Click { x: 1, y: 2 };
let b = Event::Click { x: 9, y: 9 };
let c = Event::Key('q');
println!(
"{a:?} vs {b:?}: same variant? {}",
mem::discriminant(&a) == mem::discriminant(&b)
);
println!(
"{a:?} vs {c:?}: same variant? {}",
mem::discriminant(&a) == mem::discriminant(&c)
);
}$ cargo run --bin 52_mem -- sizes -- u8 1 i32 4 u64 8 u128 16 char 4 bool 1 &u8 8 &[u8] 16 &str 16 Box<u8> 8 String 24 Vec<u64> 24 Option<String> 24 () 0 [u32; 4] 16 (u8, u32) 8 align u8 1 u32 4 u64 8 -- layout -- Padded 16 bytes (fields u8, u64, u8) size_of_val(&v) 24 (the Vec header) slice 40 size_of_val("hello") 5 -- swap / replace -- 2 1 ["y", "x"] old 2 now 9 -- take -- [1, 2, 3] left [] flushed ["a", "b"] old title draft now Buffer { lines: [], title: "final" } Option::take Some(5) left None -- replace in a state machine -- running build done build ok=true done build ok=true -- drop / forget -- drop a needs_drop String true i32 false -- discriminant -- Click { x: 1, y: 2 } vs Click { x: 9, y: 9 }: same variant? true Click { x: 1, y: 2 } vs Key('q'): same variant? false
Full program
// 52. std::mem: sizes, swap, replace, take, drop, forget
//
// size_of::<T>() is the size of a type in bytes, and align_of::<T>() its alignment. Both
// are in the prelude. size_of_val looks at a value, which matters for slices and other
// unsized data. Rust may reorder struct fields to reduce padding.
//
// The three functions for moving values out of a &mut are the most useful part of the
// module. swap(a, b) exchanges two values. replace(dest, new) puts a new value in and
// hands back the old one. take(dest) is replace with Default::default(), leaving an
// empty Vec or String behind. They let you move out of a struct field you only borrow.
//
// drop(v) ends a value early. forget(v) never runs its destructor, leaking it; that is
// safe but rarely what you want. discriminant tells two enum values' variants apart
// without comparing their data.
//
// Run: cargo run --bin 52_mem
use std::mem;
// Example 1: sizes and alignment.
fn demo_sizes() {
println!("-- sizes --");
println!(
"u8 {} i32 {} u64 {} u128 {} char {} bool {}",
size_of::<u8>(),
size_of::<i32>(),
size_of::<u64>(),
size_of::<u128>(),
size_of::<char>(),
size_of::<bool>()
);
println!(
"&u8 {} &[u8] {} &str {} Box<u8> {}",
size_of::<&u8>(),
size_of::<&[u8]>(),
size_of::<&str>(),
size_of::<Box<u8>>()
);
println!(
"String {} Vec<u64> {} Option<String> {}",
size_of::<String>(),
size_of::<Vec<u64>>(),
size_of::<Option<String>>()
);
println!(
"() {} [u32; 4] {} (u8, u32) {}",
size_of::<()>(),
size_of::<[u32; 4]>(),
size_of::<(u8, u32)>()
);
println!(
"align u8 {} u32 {} u64 {}",
align_of::<u8>(),
align_of::<u32>(),
align_of::<u64>()
);
}
#[allow(dead_code)]
struct Padded {
a: u8,
b: u64,
c: u8,
}
// Example 2: field reordering and size_of_val.
fn demo_layout() {
println!("-- layout --");
// In declaration order this would be 24 bytes; Rust reorders to 16.
println!("Padded {} bytes (fields u8, u64, u8)", size_of::<Padded>());
let v = vec![1u32; 10];
println!(
"size_of_val(&v) {} (the Vec header) slice {}",
size_of_val(&v),
size_of_val(&v[..])
);
println!("size_of_val(\"hello\") {}", size_of_val("hello"));
}
// Example 3: swap and replace.
fn demo_swap_replace() {
println!("-- swap / replace --");
let mut a = 1;
let mut b = 2;
mem::swap(&mut a, &mut b);
println!("{a} {b}");
let mut v = vec![String::from("x"), String::from("y")];
v.swap(0, 1); // for elements of one slice, use the slice method
println!("{v:?}");
let old = mem::replace(&mut a, 9);
println!("old {old} now {a}");
}
// Example 4: take and replace for moving out of a borrowed struct.
#[derive(Debug, Default)]
struct Buffer {
lines: Vec<String>,
title: String,
}
impl Buffer {
// Hand the lines to the caller and leave an empty Vec behind.
fn flush(&mut self) -> Vec<String> {
mem::take(&mut self.lines)
// self.lines // error[E0507]: cannot move out of `self.lines` which is behind a mutable reference
}
fn rename(&mut self, new: &str) -> String {
mem::replace(&mut self.title, new.to_string())
}
}
fn demo_take() {
println!("-- take --");
let mut v = vec![1, 2, 3];
let taken = mem::take(&mut v);
println!("{taken:?} left {v:?}");
let mut buf = Buffer { lines: vec!["a".into(), "b".into()], title: "draft".into() };
let flushed = buf.flush();
let old_title = buf.rename("final");
println!("flushed {flushed:?} old title {old_title} now {buf:?}");
let mut opt = Some(5);
println!("Option::take {:?} left {opt:?}", opt.take());
}
// Example 5: a state machine that moves out of self with replace.
enum State {
Idle,
Running { job: String },
Done { job: String, ok: bool },
}
impl State {
fn advance(&mut self) {
*self = match mem::replace(self, State::Idle) {
State::Idle => State::Running { job: "build".into() },
State::Running { job } => State::Done { job, ok: true },
done @ State::Done { .. } => done,
};
}
fn label(&self) -> String {
match self {
State::Idle => "idle".into(),
State::Running { job } => format!("running {job}"),
State::Done { job, ok } => format!("done {job} ok={ok}"),
}
}
}
fn demo_state_replace() {
println!("-- replace in a state machine --");
let mut s = State::Idle;
for _ in 0..3 {
s.advance();
println!("{}", s.label());
}
}
struct Loud(&'static str);
impl Drop for Loud {
fn drop(&mut self) {
println!(" drop {}", self.0);
}
}
// Example 6: drop, forget, and needs_drop.
fn demo_drop_forget() {
println!("-- drop / forget --");
let a = Loud("a");
let b = Loud("b");
drop(a);
mem::forget(b); // b's destructor never runs
println!(
" needs_drop String {} i32 {}",
mem::needs_drop::<String>(),
mem::needs_drop::<i32>()
);
}
// Example 7: comparing enum variants with discriminant.
#[derive(Debug)]
#[allow(dead_code)] // the fields are only here to show that discriminant ignores them
enum Event {
Click { x: i32, y: i32 },
Key(char),
}
fn demo_discriminant() {
println!("-- discriminant --");
let a = Event::Click { x: 1, y: 2 };
let b = Event::Click { x: 9, y: 9 };
let c = Event::Key('q');
println!(
"{a:?} vs {b:?}: same variant? {}",
mem::discriminant(&a) == mem::discriminant(&b)
);
println!(
"{a:?} vs {c:?}: same variant? {}",
mem::discriminant(&a) == mem::discriminant(&c)
);
}
fn main() {
demo_sizes();
demo_layout();
demo_swap_replace();
demo_take();
demo_state_replace();
demo_drop_forget();
demo_discriminant();
}