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 implementsCopy, or moves the field out ofsif it does not implementCopy.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 (requiringsto be declaredmut).
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