Skip to content

Core Concept: Structs & Enums ​

Updated Mar 2026

Overview ​

Structs and enums are Rust's primary data modeling tools. Structs group related fields. Enums represent a value that can be one of several variants. Combined with impl blocks, they form Rust's approach to data + behavior.

Struct Types ​

Named Field Structs ​

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

fn main() {
    // Create instance
    let user1 = User {
        email: String::from("alice@example.com"),
        username: String::from("alice"),
        active: true,
        sign_in_count: 1,
    };

    // Field init shorthand (variable name matches field name)
    let email = String::from("bob@example.com");
    let username = String::from("bob");
    let user2 = User {
        email,     // Same as email: email
        username,  // Same as username: username
        active: true,
        sign_in_count: 1,
    };

    // Struct update syntax (spread remaining fields)
    let user3 = User {
        email: String::from("charlie@example.com"),
        ..user2  // Take remaining fields from user2 (user2 partially moved!)
    };
}

Tuple Structs ​

rust
struct Color(u8, u8, u8);
struct Point(f64, f64, f64);

fn main() {
    let red = Color(255, 0, 0);
    let origin = Point(0.0, 0.0, 0.0);

    // Access by index
    println!("Red: {}", red.0);
    println!("X: {}", origin.0);

    // Destructure
    let Color(r, g, b) = red;
    println!("RGB: {r}, {g}, {b}");
}

Unit Structs ​

rust
struct AlwaysEqual;

// Useful for trait implementations without data
impl PartialEq for AlwaysEqual {
    fn eq(&self, _other: &Self) -> bool {
        true
    }
}

impl Blocks ​

rust
#[derive(Debug)]
struct Rectangle {
    width: f64,
    height: f64,
}

impl Rectangle {
    // Associated function (constructor — no self)
    fn new(width: f64, height: f64) -> Self {
        Rectangle { width, height }
    }

    fn square(size: f64) -> Self {
        Rectangle { width: size, height: size }
    }

    // Method (borrows self)
    fn area(&self) -> f64 {
        self.width * self.height
    }

    fn perimeter(&self) -> f64 {
        2.0 * (self.width + self.height)
    }

    // Method that mutates
    fn scale(&mut self, factor: f64) {
        self.width *= factor;
        self.height *= factor;
    }

    // Method that takes ownership
    fn into_square(self) -> Rectangle {
        let side = (self.width + self.height) / 2.0;
        Rectangle::square(side)
    }

    fn can_hold(&self, other: &Rectangle) -> bool {
        self.width > other.width && self.height > other.height
    }
}

fn main() {
    let mut rect = Rectangle::new(30.0, 50.0);
    println!("Area: {}", rect.area());

    rect.scale(2.0);
    println!("Scaled area: {}", rect.area());

    let square = Rectangle::square(10.0);
    println!("Can hold square: {}", rect.can_hold(&square));
}

Enums ​

rust
#[derive(Debug)]
enum Message {
    Quit,                          // No associated data
    Move { x: i32, y: i32 },      // Named fields (like a struct)
    Write(String),                 // Single unnamed field
    ChangeColor(u8, u8, u8),      // Multiple unnamed fields
}

impl Message {
    fn call(&self) {
        match self {
            Message::Quit => println!("Quitting"),
            Message::Move { x, y } => println!("Moving to ({x}, {y})"),
            Message::Write(text) => println!("Writing: {text}"),
            Message::ChangeColor(r, g, b) => println!("Color: ({r}, {g}, {b})"),
        }
    }
}

fn main() {
    let messages = vec![
        Message::Quit,
        Message::Move { x: 10, y: 20 },
        Message::Write("hello".to_string()),
        Message::ChangeColor(255, 0, 128),
    ];

    for msg in &messages {
        msg.call();
    }
}

Option<T> ​

rust
fn find_user(id: u32) -> Option<String> {
    match id {
        1 => Some("Alice".to_string()),
        2 => Some("Bob".to_string()),
        _ => None,
    }
}

fn main() {
    // Pattern matching
    match find_user(1) {
        Some(name) => println!("Found: {name}"),
        None => println!("Not found"),
    }

    // if let
    if let Some(name) = find_user(2) {
        println!("Found: {name}");
    }

    // Combinators
    let name = find_user(1)
        .map(|n| n.to_uppercase())        // Transform if Some
        .unwrap_or("unknown".to_string()); // Default if None

    let name = find_user(99)
        .unwrap_or_default();  // String::default() = ""

    // Chaining Options
    let result = find_user(1)
        .and_then(|name| {
            if name.len() > 3 { Some(name) } else { None }
        });

    // unwrap (panics if None — use only in tests/examples)
    let name = find_user(1).unwrap();
    let name = find_user(1).expect("User 1 must exist");
}

Result<T, E> ​

rust
use std::num::ParseIntError;

fn parse_number(s: &str) -> Result<i32, ParseIntError> {
    s.parse::<i32>()
}

fn double_parse(s: &str) -> Result<i32, String> {
    let n = s.parse::<i32>()
        .map_err(|e| format!("Parse error: {e}"))?;
    Ok(n * 2)
}

fn main() {
    // Pattern matching
    match parse_number("42") {
        Ok(n) => println!("Parsed: {n}"),
        Err(e) => println!("Error: {e}"),
    }

    // Combinators
    let result = parse_number("42")
        .map(|n| n * 2)                    // Transform if Ok
        .unwrap_or(0);                      // Default if Err

    // Converting Result to Option
    let opt: Option<i32> = parse_number("42").ok();   // Some(42)
    let opt: Option<i32> = parse_number("abc").ok();  // None

    // Converting Option to Result
    let result: Result<i32, &str> = Some(42).ok_or("missing value");
}

Enum with Methods (State Machine) ​

rust
enum TrafficLight {
    Red,
    Yellow,
    Green,
}

impl TrafficLight {
    fn duration_seconds(&self) -> u32 {
        match self {
            TrafficLight::Red => 60,
            TrafficLight::Yellow => 5,
            TrafficLight::Green => 45,
        }
    }

    fn next(&self) -> TrafficLight {
        match self {
            TrafficLight::Red => TrafficLight::Green,
            TrafficLight::Yellow => TrafficLight::Red,
            TrafficLight::Green => TrafficLight::Yellow,
        }
    }
}

Combining Structs and Enums ​

rust
#[derive(Debug)]
struct Order {
    id: u64,
    items: Vec<String>,
    status: OrderStatus,
}

#[derive(Debug)]
enum OrderStatus {
    Pending,
    Processing { estimated_minutes: u32 },
    Shipped { tracking: String },
    Delivered,
    Cancelled { reason: String },
}

impl Order {
    fn is_active(&self) -> bool {
        !matches!(self.status, OrderStatus::Delivered | OrderStatus::Cancelled { .. })
    }
}

Built with VitePress | Rust SOP Documentation