CodeBlanks Logo Icon CodeBlanks

ZST

Learn zero-sized types (ZSTs) and their advantages.

ZSTs

Zero-sized types take no memory at all: (), structs without fields, [T; 0] and PhantomData<T>. The compiler erases them entirely; Vec<()> makes no allocation, and HashMap<K, ()> takes no more room than a HashSet:

size_of::<()>()                    // 0
size_of::<PhantomData<u64>>()      // 0
let v: Vec<()> = vec![(); 1_000_000];   // allocates no memory

ZSTs are ideal for carrying type-level information: marker types, state machines (typestate), unit systems.

Unit safety

Zero-cost unit tags separate values that carry the same number but must not be mixed up:

use std::marker::PhantomData;

struct Metre;
struct Saniye;

struct Olcu<B> {
    deger: f64,
    _birim: PhantomData<B>,
}

fn yol(m: Olcu<Metre>) -> f64 { m.deger }
// yol(Olcu::<Saniye> { .. })   // error: unit mismatch

In memory, Olcu<Metre> is just an f64.

Practice Zero-sized types

Write the Olcu<B> type above and the Metre unit. Print size_of::<()>(), size_of::<PhantomData<u64>>() and size_of::<Olcu<Metre>>() separated by spaces.

Starter code

main.rs
use std::marker::PhantomData;
use std::mem::size_of;

fn main() {
    // buraya yaz
}

Sample solution

main.rs
#![allow(dead_code)]
use std::marker::PhantomData;
use std::mem::size_of;

struct Metre;

struct Olcu<B> {
    deger: f64,
    _birim: PhantomData<B>,
}

fn main() {
    println!("{} {} {}", size_of::<()>(), size_of::<PhantomData<u64>>(), size_of::<Olcu<Metre>>());
}

Expected output

0 0 8

Questions and answers

Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.

1 Why is <code>PhantomData&lt;T&gt;</code> needed?

  • To tell the compiler about an unused type parameter, "I own/depend on this type" Correct answer
  • To allocate memory
  • For speed
  • It isn't needed

Explanation: An unused type parameter is a compile error (E0392); PhantomData also gives the right information for drop checking and variance.

2 Phantom type

Question
struct Olcu<B> {
    deger: f64,
    _birim: ____<B>,
}
Answer
struct Olcu<B> {
    deger: f64,
    _birim: PhantomData<B>,
}

Explanation: PhantomData<B> is zero-sized but uses the type parameter B.

3 Type and size

Term Meaning
() 0
PhantomData<u64> 0 bytes
Olcu<Metre> 8
[u8; 0] Zero (array)

All lessons in Expert