Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

The Shape of Data

If you only remember one thing: Rust has two ways to hold data together — a struct when you need "all of these at once", and an enum when you need "exactly one of these".

Structs: one record with named fields

A struct is a named bag of fields.

struct User {
    name: String,
    age: u32,
    active: bool,
}

fn main() {
    let ada = User {
        name: String::from("Ada"),
        age: 36,
        active: true,
    };

    println!("{} is {}", ada.name, ada.age);
}

▶ Run this in the Rust Playground

Plain English

A struct is a Python dict with the keys decided up front and the compiler enforcing that you did not forget one or misspell one. It is a TypeScript interface but the fields actually exist in memory.

A struct lays out its fields contiguously in memory. Fast to access, zero hidden allocations, exactly the size of its parts plus any padding the CPU needs.

Enums: exactly one of the listed options

#![allow(unused)]
fn main() {
enum Status {
    Online,
    Offline,
    Busy,
}
}

That enum says: a Status is one of those three, and the compiler will never let it be anything else.

Rust enums go further than C enums — each variant can carry data of its own:

#![allow(unused)]
fn main() {
enum Event {
    Click { x: i32, y: i32 },
    KeyPress(char),
    Disconnect,
}
}

A Click carries two coordinates. A KeyPress carries one character. A Disconnect carries nothing. But an Event is always one of those three, never two of them, never none.

Two shapes

struct = "and". enum = "or".

struct User name age active all at once enum Status Online Offline Busy exactly one at a time let s: Status = Status::Online;
A User has a name and an age and a flag. A Status is online or offline or busy.

Matching on an enum

Once you have an enum, Rust wants you to handle every case. You do that with match:

enum Status {
    Online,
    Offline,
    Busy,
}

fn greet(s: Status) -> &'static str {
    match s {
        Status::Online  => "welcome back",
        Status::Offline => "see you soon",
        Status::Busy    => "do not disturb",
    }
}

fn main() {
    println!("{}", greet(Status::Online));
}

▶ Run this in the Rust Playground

If you delete a branch — say, Busy — Rust will refuse to compile until you handle it:

error[E0004]: non-exhaustive patterns: `Busy` not covered

That is the feature. You cannot forget a case.

Analogy

Pattern matching on an enum is a multiple-choice question where the compiler refuses to let you turn in the test with blanks. It will tell you which blanks you left, by name.

Tuples and arrays: the two lightweight containers

When you need a small, fixed group of values and do not want to bother naming fields, use a tuple:

#![allow(unused)]
fn main() {
let point = (3, 4);
let (x, y) = point;
println!("{x}, {y}");
}

When you need a fixed-size collection of values of the same type, use an array:

#![allow(unused)]
fn main() {
let week = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
println!("{}", week[0]);
}

For a growable list, you want Vec<T>. We will meet Vec in Chapter 7.

Methods on a struct

You attach behavior to a struct with impl:

struct Rect { width: f64, height: f64 }

impl Rect {
    fn area(&self) -> f64 {
        self.width * self.height
    }
}

fn main() {
    let r = Rect { width: 3.0, height: 4.0 };
    println!("{}", r.area());
}

▶ Run this in the Rust Playground

&self is the method’s way of saying “I will look at the struct but not take it over.” You will see &self on almost every method you ever write. Its meaning will be the subject of the next two chapters.

Here is where a struct actually lives. Step through and watch the User value take shape in memory.

Interactive simulation (requires JavaScript): a struct definition uses no memory; constructing a User places its three fields contiguously on the stack, with the String field pointing at a heap buffer that the struct owns.

## Try this
Five-minute exercises
  1. Define a struct Book { title: String, pages: u32 } and print one.
  2. Define an enum Shape with variants Circle(f64) and Square(f64). Write a function area(s: Shape) -> f64 using match.
  3. Delete one of the match branches in that function. Read the error.

Next, the idea that makes Rust Rust.

Ownership in one page →