Skip to content

The structs map

Level: reference · the map

One line: A struct names a group of values and makes that name a type — and this page is the door to every lesson about structs, in the order the questions actually come up.

Structs are the type you define on your first day and are still designing in your third month, so the lessons are spread across the library rather than gathered in one folder. What follows is a reading order, not a syllabus; start where your question is.

If you just want to know what a struct is, read What a struct is — the tables below are the route, not the explanation.


Why this is a page and not a folder

Because folders are permanent URLs. A topic — the newtype, Option fields, lifetimes — is stable for years; a sequence is not, and the moment a lesson belongs between §II and §III a numbered folder either gets renumbered (breaking every link anyone saved) or starts lying about its own order.

So the library keeps a strict split, and it is worth knowing which surface answers which question:

Surface Holds Example
a topic folder one idea, with a runnable program 16_Structs/what_a_struct_is/
this map the reading order, and what is still missing you are here
KATAS.md the exercises, numbered — the numbers live nowhere else K52
GLOSSARY.md the vocabulary, one line each, linking the page associated function

Same rule the sidebar follows: order is presentation, so it belongs in a page, never in a path. OPTION.md, SHADOWING.md and STRINGS.md are the other maps.

The territory, as a picture

Four diagrams, because a map of the vocabulary is a different question from "which one do I write here?". They render on GitHub and on the site from the same source.

1. The whole territory, and its keywords

Everything a struct question turns out to be about, in one frame. If a term below is unfamiliar, GLOSSARY.md defines it in a line.

flowchart LR
    S["struct<br/>names a group of values,<br/>and makes that name a type"]

    S --> SHAPE["SHAPE<br/>what the declaration looks like"]
    S --> BEHAV["BEHAVIOUR<br/>lives in impl, never in the body"]
    S --> DATA["DATA<br/>what the fields let you do"]
    S --> VIS["VISIBILITY<br/>who is allowed to look"]
    S --> MEM["MEMORY<br/>what it actually costs"]
    S --> GEN["GENERICS<br/>one struct, many types"]

    SHAPE --> N1["named fields"]
    SHAPE --> N2["tuple struct, and the newtype"]
    SHAPE --> N3["unit struct"]

    BEHAV --> B1["associated function<br/>no self — Type::new"]
    BEHAV --> B2["method<br/>takes a self receiver — value.thing"]
    BEHAV --> B3["inherent impl vs trait impl"]

    DATA --> D1["Copy and Clone"]
    DATA --> D2["Default, and ..base update syntax"]
    DATA --> D3["Debug and Display"]
    DATA --> D4["PartialEq, Eq, PartialOrd, Ord"]

    VIS --> V1["pub is per FIELD, not per struct"]
    VIS --> V2["privacy is per MODULE"]
    VIS --> V3["non_exhaustive, for published types"]

    MEM --> M1["field order is NOT guaranteed"]
    MEM --> M2["padding, alignment, size"]
    MEM --> M3["repr C, when the layout is a contract"]

    GEN --> G1["type parameters"]
    GEN --> G2["const generics"]
    GEN --> G3["associated types — on TRAITS, not structs"]
    GEN --> G4["a reference in a field needs a lifetime"]

2. Which flavour do I declare?

flowchart TD
    Q1{"Does it hold any data?"}
    Q1 -- no --> UNIT["UNIT STRUCT<br/>struct Marker;<br/>a name with no bytes"]
    Q1 -- yes --> Q2{"Is it wrapping exactly one existing<br/>type in order to give it a job?"}
    Q2 -- yes --> NEW["NEWTYPE<br/>struct Score of u8<br/>a distinct type, not an alias"]
    Q2 -- no --> Q3{"Would a reader need the parts named?"}
    Q3 -- yes --> NAMED["NAMED FIELDS<br/>the default, and usually right"]
    Q3 -- no --> TUP["TUPLE STRUCT<br/>positional, for 2 or 3 obvious parts"]

The newtype branch is the one people skip and then want back: A score is not a number, and why an alias gives no safety at all.

3. Which receiver do I write?

The decision that causes the most compiler errors, and the one most tutorials get slightly wrong.

flowchart TD
    Z{"Is this function about THIS type?"}
    Z -- "no, it spans several types<br/>or belongs to your domain" --> FF["FREE FUNCTION<br/>module scope, called as feel<br/>invisible from the type"]
    Z -- yes --> A{"Does it need an existing instance at all?"}
    A -- no --> AF["ASSOCIATED FUNCTION<br/>no self parameter<br/>called as Type::new"]
    A -- yes --> B{"Does it change the value?"}
    B -- no --> R1["&self<br/>reads it — caller keeps it<br/>many of these can run at once"]
    B -- yes --> C{"Should the caller still have it afterwards?"}
    C -- yes --> R2["&mut self<br/>changes it in place<br/>caller needs a mut binding"]
    C -- no --> R3["self<br/>consumes it — the value's life<br/>ends here, deliberately"]
    R3 --> D{"Do you assign to a field inside the body?"}
    D -- yes --> R4["mut self<br/>SAME receiver, plus a mutable binding"]
    D -- no --> R5["self"]

A free function that returns a Feelings and does nothing else is an associated function written in the wrong place. Nothing about the behaviour differs — what differs is that Feelings::new() is found by typing Feelings::, appears in the type's documentation, and can satisfy a trait requirement, while a free feel() does none of the three.

mut self is not a fourth receiver. It is self with a mutable binding, exactly like fn f(mut x: T). The caller cannot see the difference — a trait that declares fn consume(self) may be implemented as fn consume(mut self), and calling a mut self method needs no mut on the caller's binding. Both facts are compiled, not asserted.

The rarer spellings — self: Box<Self>, Rc<Self>, Arc<Self>, Pin<&mut Self>are real receivers and do change what the caller must hand over. The Reference ↗ gives the full grammar.

4. The life of one instance

flowchart LR
    N["Type::new — associated function"] --> V["a value you own"]
    V -- "&" --> B["shared borrow<br/>many readers, no writers"]
    V -- "&mut" --> M["exclusive borrow<br/>one writer, no readers"]
    V -- "..base" --> U["struct update<br/>moves field by field —<br/>the base ends up PARTIALLY dead"]
    V -- "a method taking self" --> C["consumed<br/>using it again is E0382"]
    B --> V
    M --> V
    C --> DR["Drop runs"]
    V --> DR

Drop is skipped by std::process::abort and by std::mem::forget — running a destructor is a strong default, not a guarantee.

Start here

# Lesson Level The question it answers
1 What a struct is 101 → 201 The three flavors, impl vs the struct body, associated function vs method, and why privacy is per module
2 impl blocks 101 → 201 Where the functions went — associated function vs method, the three receivers, and inherent vs trait impl
3 A score is not a number: the newtype 101 → 201 The tuple struct with a job — one checked door, and the invalid value stops existing
4 Option fields 101 Required by default, and the one question that decides whether a field should be optional at all
5 Debug and Display 101 → 201 Why {} refuses your struct and {:?} does not — the first derive you will reach for
When a struct refuses 101 → 201 The eight errors you will actually hit — and why E0277 names four unrelated problems
What dbg! does 101 → 201 Five things it does that println!("{:?}") does not — and why a field prints when the struct will not

The data inside

Lesson Level What it teaches
Copy vs Clone 101 → 201 Why a struct is never Copy by accident, and the three refusals — E0277, E0204, E0184
Struct update syntax 101 → 201 ..base moves field by field, so the base ends up partially dead — and Copy decides which half
Some is a constructor, not a flag 101 → 201 Some(None) in a field, and the doubly-optional type that makes it legal
What is a record, in memory? 201 Designing a real struct: the layout choices, and the parallel Vecs that desync
Six kinds of zero 201 When a field's zero is a value and when it is a hole
Bit flags 201 Packing several fields into one integer, and when that is worth doing
The Result you are reading is probably an alias 201 The other way to name a type — and why an alias gives no safety at all

Ownership, borrowing, lifetimes

A struct owning its fields is the default, and the reason String shows up in beginner code where &str looks tidier.

Lesson Level What it teaches
Ownership and moves 101 A move transfers responsibility — what happens when a struct owns its fields
Borrowing 101 → 201 &T and &mut T, and the rule that decides which order compiles
How to learn lifetimes 201 Is "clone everything" good advice? Mostly — with three amendments
A name is not a place 201 Why mut belongs to the binding, which is why you cannot mark one field mutable

Deriving, and the traits you get for free

Lesson Level What it teaches
unwrap_or_default 201 A derived Default is the type's zero, not your domain's — and struct update syntax
The Default trait 201 ..Default::default() and the config struct built out of it — stub, an outline only
serde derive 201 The derive that turns a struct into a wire format — stub, an outline only
clap derive 201 A struct that is the command-line interface — stub, an outline only
Comments that compile 101 → 201 /// on a field or a method is #[doc], and its examples are run as tests

Structs in the wild

Lesson Level What it teaches
Units are types 201 The newtype used in anger: a quantity that cannot be added to the wrong quantity — stub, an outline only
The right to post is a value 301 A struct modelling an entitlement, and spending it exactly once
Lock poisoning 301 A struct shared across threads, and the Result the lock hands you

Still missing

Named honestly, because a map that only lists what exists is a map of the wrong territory. Each becomes a page once it has a runnable example worth reading — rough order, not a promise:

  • Destructuring a struct — in a let, in a match arm, and in a function parameter
  • Comparing structsPartialEq / Eq / PartialOrd / Ord, and the derive that compares fields in declaration order
  • Generic structs — now 22_Generics/: the parameter list, the E0282 when nothing fills it in, and where the trait bound belongs. Still missing from there: operator overloading, so Point<T> + Point<T> needs T: Add<Output = T>
  • Implementing a trait for your struct — including default methods, and why this replaces inheritance
  • A reference in a fieldE0106 in full, struct User<'a>, and what 'static does and does not promise
  • Enum variants that carry structs — the nesting that makes a state machine, and matching it
  • The builder pattern — and the simpler thing that usually beats it
  • Const genericsstruct Board<const PINS: usize>, and why the impl needs the parameter too
  • Associated types — and the trap that they live on traits, never in a struct body
  • Zero-sized typesstruct Nothing; costs no bytes, and why Set<K> = Map<K, ()> is free
  • Layout, padding and repr — why String, String, u8 is 56 bytes and not 49, and when field order becomes a contract
  • Swapping two fieldsmem::swap needs one type, so two &mut into different fields is the interesting half

If you want one of these next, that is the list to point at.

The thing that is shaped like a struct but is not one

A union is declared exactly like a struct and behaves nothing like one: its fields share a single slot rather than sitting side by side. It is worth reading once, because the punchline settles a question this map raises twice — a Rust enum is a union with a tag the compiler makes you read, which is why you can model a choice without ever writing unsafe.

The material that is not in this library

Structs: the shelf is the checked list of outside material — the Book chapter worth reading, the Reference pages that settle an argument, the videos with their chapter marks, and the four links that have rotted since they were collected.

Looking a term up

GLOSSARY.md defines the vocabulary these pages use — associated function, unit struct, tuple struct, field init shorthand, struct update syntax, newtype — and every entry links to the page that explains it properly.

Po polsku

Struktura (struct) nazywa grupę wartości i czyni z tej nazwy typ. Ta strona jest mapą wszystkich lekcji o strukturach, rozrzuconych po całej bibliotece — bo strukturę definiuje się pierwszego dnia, a projektuje jeszcze w trzecim miesiącu.

Dla kogoś, kto przychodzi z języka obiektowego, najważniejsze jest jedno rozstrzygnięcie: struktura to nie klasa. Nie ma dziedziczenia, metody nie mieszczą się w samej definicji (idą do osobnego bloku impl), i nie istnieje nic, co odpowiadałoby konstruktorowi jako elementowi języka. Type::new() to zwykła funkcja powiązana, tak nazwana z konwencji — nazwa nie jest w żaden sposób wyróżniona przez kompilator i można ją nazwać inaczej.

Z polskiej terminologii przydają się: pole (field), struktura krotkowa (tuple struct, Score(u8)), pusta struktura (unit struct) oraz nowy typ (newtype). To ostatnie nie jest konstrukcją języka, tylko wzorcem: jednopolowa struktura opakowująca inny typ po to, żeby kompilator przestał mylić punkty z identyfikatorami.

Szukaj po polsku: struktury w Ruscie · bloki impl · struktura krotkowa · wzorzec nowego typu · rust struct vs class