Hands-On Concurrency with Rust
上QQ阅读APP看书,第一时间看更新

The size of a type

There's two important things here we have not come across yet in this bookSized and Unique. First, Sized, or more properly, std::marker::Sized. Sized, is a Rust trait that bounds a type to have a known size at compile time. Almost everything in Rust has an implicit Sized bound, which we can inspect. For instance:

use std::mem;

#[allow(dead_code)]
enum Project {
    Mercury { mission: u8 },
    Gemini { mission: u8 },
    Apollo { mission: u8 },
    Shuttle { mission: u8 },
}

fn main() {
    assert_eq!(1, mem::size_of::<u8>());
    assert_eq!(2, mem::size_of::<Project>());

    let ptr_sz = mem::size_of::<usize>();
    assert_eq!(ptr_sz, mem::size_of::<&Project>());

    let vec_sz = mem::size_of::<usize>() * 2 + ptr_sz;
    assert_eq!(vec_sz, mem::size_of::<Vec<Project>>());
}

u8 is a single byte, Project is the byte to distinguish the enum variants plus the inner mission byte, pointers are the size of a machine word–a usize- and a Vec<T> is guaranteed to be a pointer and two usize fields no matter the size of T. There's something interesting going on with Vec<T> and we'll get into it in depth later in this chapter. Note that we said almost everything in Rust has an implicit Sized bound. Rust supports dynamically sized types, these being types that have no known size or alignment. Rust requires known size and alignment and so all DSTs must exist behind a reference or pointer. A slice—a view into contiguous memory—is one such type. In the following program, the compiler will not be able to determine the size of the slice of values:

use std::time::{SystemTime, UNIX_EPOCH};

fn main() {
    let values = vec![0, 1, 2, 3, 4, 5, 7, 8, 9, 10];
    let cur: usize = SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .unwrap()
        .as_secs() as usize;
    let cap: usize = cur % values.len();

    let slc: &[u8] = &values[0..cap];

    println!("{:?}", slc);
}

Slices are a view into a block of memory represented as a pointer and a length, as the documentation for the primitive type slice puts it. The trick is that the length is determined at runtime. The following program will compile:

fn main() {
    let values = vec![0, 1, 2, 3, 4, 5, 7, 8, 9, 10];
    let slc: &[u8] = &values[0..10_000];

    println!("{:?}", slc);
}

However, the previous code will panic at runtime:

> ./past_the_end thread 
'main' panicked at 'index 10000 out of range for slice of length 10', libcore/slice/mod.rs:785:5 note: Run with `RUST_BACKTRACE=1` for a backtrace.

Rust allows programmers to include DSTs as the last field of a struct, like so:

struct ImportantThing {
  tag: u8,
  data: [u8],
}

However, this causes the struct itself to become a DST.