Rust Structs

A struct (short for structure) lets you group related pieces of data under one name. Instead of managing separate variables for every property, you pack them into one custom type that represents a meaningful concept.

Defining a Struct

struct Car {
    brand: String,
    year: u32,
    is_electric: bool,
}

This defines a new type called Car. It has three fields: a brand name, a year, and a flag for whether it runs on electricity.

Creating an Instance

fn main() {
    let my_car = Car {
        brand: String::from("Toyota"),
        year: 2022,
        is_electric: false,
    };

    println!("{} ({})", my_car.brand, my_car.year);
}

Output:

Toyota (2022)

Access fields using dot notation: my_car.brand, my_car.year.

The Form Diagram

struct Car {              ← The blank form template
    brand: String,
    year: u32,
    is_electric: bool,
}

let my_car = Car {        ← A filled-out form (instance)
    brand: "Toyota",
    year: 2022,
    is_electric: false,
};

Mutable Struct Instances

To change a field after creation, the whole instance must be declared mut. Rust does not allow marking individual fields as mutable.

let mut my_car = Car {
    brand: String::from("Toyota"),
    year: 2022,
    is_electric: false,
};

my_car.year = 2024;   ← Update the year

Struct Update Syntax

Create a new instance that copies most fields from an existing one, only overriding what changes:

let car2 = Car {
    year: 2023,
    ..my_car          ← Copy all other fields from my_car
};

Methods on Structs

Methods are functions that belong to a struct. Define them inside an impl block. The first parameter is always &self, which refers to the instance the method is called on.

struct Rectangle {
    width: u32,
    height: u32,
}

impl Rectangle {
    fn area(&self) -> u32 {
        self.width * self.height
    }

    fn is_square(&self) -> bool {
        self.width == self.height
    }
}

fn main() {
    let r = Rectangle { width: 10, height: 5 };
    println!("Area: {}", r.area());
    println!("Square: {}", r.is_square());
}

Output:

Area: 50
Square: false

Associated Functions

Functions inside impl that do not take &self are called associated functions. They work like constructors in other languages. Call them with :: notation:

impl Rectangle {
    fn square(size: u32) -> Rectangle {
        Rectangle { width: size, height: size }
    }
}

let sq = Rectangle::square(10);

Tuple Structs

A tuple struct gives a tuple a meaningful name without naming each field:

struct Color(u8, u8, u8);   ← Red, Green, Blue

let red = Color(255, 0, 0);
println!("Red channel: {}", red.0);

Printing a Struct for Debugging

Add #[derive(Debug)] above your struct to enable debug printing with {:?}:

#[derive(Debug)]
struct Car {
    brand: String,
    year: u32,
}

fn main() {
    let c = Car { brand: String::from("Honda"), year: 2020 };
    println!("{:?}", c);
}

Output:

Car { brand: "Honda", year: 2020 }

Quick Reference

struct Name { field: Type }         ← Define a struct
let x = Name { field: value }       ← Create an instance
x.field                             ← Access a field
impl Name { fn method(&self) }      ← Add a method
Name::function()                    ← Call an associated function

Leave a Comment