Structs

Field Access

Field access reads, mutates, borrows, or moves an individual field of a struct using dot notation (instance.field for named-field structs and instance.0 for tuple structs) or pattern destructuring.

Dot Notation and Automatic Dereferencing

When accessing a field with the . operator (point.x), Rust automatically dereferences any number of references or smart pointers (&T, &mut T, Box<T>, Rc<T>, Arc<T>) to reach the underlying struct. You never need to write (*point_ref).x or point_ref->x:

Receiver Expression Explicit Form Idiomatic Rust Form Result
p: Point (by value) p.x p.x Accesses x directly on p
r: &Point (shared reference) (*r).x r.x Auto-dereferences r and accesses x
m: &mut Point (mutable reference) (*m).x = 10 m.x = 10 Auto-dereferences m and mutates x
b: Box<Point> (smart pointer) (*b).x b.x Auto-dereferences via Deref and accesses x
c: Color(u8, u8, u8) (tuple struct) c.0, c.1, c.2 c.0, c.1, c.2 Accesses positional fields by zero-based index
#[derive(Debug)]
struct ServerConfig {
    host: String,
    port: u16,
}

struct Millimeters(u32);

fn print_endpoint(cfg: &ServerConfig) {
    // `cfg` is `&ServerConfig`, yet `cfg.host` and `cfg.port` auto-dereference cleanly:
    println!("Endpoint: {}:{}", cfg.host, cfg.port);
}

fn main() {
    let mut cfg = Box::new(ServerConfig {
        host: String::from("127.0.0.1"),
        port: 8080,
    });

    // Mutate a field through `Box<ServerConfig>` via automatic dereferencing:
    cfg.port = 9090;
    print_endpoint(&cfg);

    // Tuple struct positional field access:
    let width = Millimeters(250);
    println!("Width: {} mm", width.0);
}
Endpoint: 127.0.0.1:9090
Width: 250 mm

Accessing Fields by Value vs. by Reference

A field access expression s.field is a place expression (an lvalue referring to a memory location). Depending on how s.field is used, Rust either copies/moves the field or borrows it:

  • let val = s.field; copies the field if its type implements Copy, or moves the field out of s if it does not implement Copy.
  • let ref_val = &s.field; creates a shared borrow of that single field without moving it.
  • let mut_val = &mut s.field; creates a mutable borrow of that single field (requiring s to be declared mut).
struct Job {
    id: u64,       // Copy
    title: String, // Non-Copy (owns heap memory)
}

fn main() {
    let job = Job {
        id: 101,
        title: String::from("Compile kernel"),
    };

    // `u64` is `Copy`, so `job.id` is copied without invalidating `job`:
    let job_id: u64 = job.id;

    // Borrow `job.title` with `&` so `job` retains ownership of the `String`:
    let title_ref: &str = &job.title;

    println!("Job #{job_id}: {title_ref}");
    println!("Full job still valid: #{} ({})", job.id, job.title);
}
Job #101: Compile kernel
Full job still valid: #101 (Compile kernel)

Accessing Multiple Fields via Destructuring

When you need local bindings for several fields at once, struct pattern destructuring in a let statement extracts or borrows multiple fields simultaneously and can ignore unneeded fields with ..:

struct Rectangle {
    x: i32,
    y: i32,
    width: u32,
    height: u32,
}

fn main() {
    let rect = Rectangle {
        x: 10,
        y: 20,
        width: 1920,
        height: 1080,
    };

    // Extract only `width` and `height`, ignoring `x` and `y` with `..`:
    let Rectangle { width, height, .. } = rect;
    println!("Resolution: {width}x{height}");
}
Resolution: 1920x1080