← Back to Rust series
🦀
Type system
Type system · Prerequisite: ownership 1-5

11. Structs (named, tuple, unit)

Structs are the most basic user-defined type — they group values under meaningful names. Rust has three flavors: named structs with field names, tuple structs with positional fields, and unit structs with no data. Methods live in separate `impl` blocks.

Ruststructimplmethodtype system
Duration
~1.5 hours
Level
📊 Intermediate
Prerequisite
🎯 Ownership 1-5
OUTCOME
Structs are the most basic user-defined type — they group values under meaningful names. Rust has three flavors: named structs with field names, tuple structs with positional fields, and unit structs with no data. Methods live in separate `impl` blocks.

What you'll learn

  • 1Define named / tuple / unit structs
  • 2Write methods and associated functions in an impl block
  • 3Use struct update syntax (`..base`) to partially copy a struct
  • 4Enable debug printing with `#[derive(Debug)]`
  • 5Distinguish &self / &mut self / self receivers

Overview

You could use `(f64, f64)` for a point — but `.0` `.1` accessors hide intent. A struct gives those same fields **meaningful names**, boosting self-documenting power.

Core Concepts

1) The three forms

rust
// named — has field names
struct Point { x: f64, y: f64 }

// tuple struct — positional, no names
struct Color(u8, u8, u8);

// unit struct — no data, marker
struct Marker;

2) impl — methods and associated functions

  • **Method** — first parameter is `self`, `&self`, or `&mut self`. Called on an instance
  • **Associated function** — no self. Called as `Type::func()`. Often used as constructors

3) self in three shapes

ReceiverMeaningUse case
&selfshared referenceread methods
&mut selfexclusive referencewrite methods
selftakes ownershiptransform / consume methods

4) Struct update syntax

`..base` builds a fresh instance by partially overriding fields from another.

Hands-on Examples

rust
#[derive(Debug, Clone)]
struct Point { x: f64, y: f64 }

impl Point {
    // associated function — constructor
    fn new(x: f64, y: f64) -> Self {
        Self { x, y }
    }
    // method — &self read
    fn distance_from_origin(&self) -> f64 {
        (self.x * self.x + self.y * self.y).sqrt()
    }
    // method — &mut self write
    fn translate(&mut self, dx: f64, dy: f64) {
        self.x += dx;
        self.y += dy;
    }
}

fn main() {
    let mut p = Point::new(3.0, 4.0);
    println!("{}", p.distance_from_origin()); // 5.0
    p.translate(1.0, 1.0);
    println!("{:?}", p);                       // Point { x: 4.0, y: 5.0 }
}

Tuple struct as a simple wrapper:

rust
struct Color(u8, u8, u8);

fn main() {
    let red = Color(255, 0, 0);
    println!("R={}", red.0);
}

Struct update syntax:

rust
#[derive(Debug)]
struct User { name: String, email: String, active: bool }

fn main() {
    let u1 = User { name: "Alice".into(), email: "a@b.c".into(), active: true };
    let u2 = User { email: "new@b.c".into(), ..u1 };  // name, active reused
    println!("{:?}", u2);
}

Common Mistakes

Q. println!("{}", p) doesn't work

A. Structs don't implement Display by default. Derive Debug and print with `{:?}` / `{:#?}`, or write `impl fmt::Display` yourself.

Q. &self vs self — which one?

A. **Read = &self, write = &mut self, transform/consume = self**. This three-way split is one of the cleanest aspects of Rust.

Q. Tuple struct vs. a plain tuple?

A. A tuple struct is a **named distinct type**. `Meters(f64)` and `Feet(f64)` won't accidentally interconvert — the compiler stops unit-mix-ups.

Recap

  • Three forms: named / tuple / unit
  • Methods and associated functions live in impl
  • Three self receivers: &self / &mut self / self
  • `..base` syntax creates partially-updated instances

Try It Yourself

  1. Write a Rectangle struct with area() and perimeter() methods
  2. Add a User::update_email(&mut self, new: String) method and call it
  3. Build tuple structs Meters(f64) / Feet(f64) and write a conversion function — observe type safety
Example code / lecture materials

All lecture materials and example code are openly available on GitHub.

View on GitHub ↗