Structs and Enums
Structs
Section titled “Structs”Structs are the primary mechanism for defining custom types in Rust. Unlike classes in C++ or Java, Structs in Rust do not support inheritance. Composition and trait-based polymorphism are the Idiomatic alternatives.
Unit Structs
Section titled “Unit Structs”Unit structs have no fields. They are useful as marker types or phantom types:
struct Marker;struct Benchmark;
impl Marker { fn describe(&self) -> &"static str { "I am a marker type" }}Unit structs have size 0 (they are zero-sized types). This makes them free to create and pass around
- the compiler optimizes away all storage for them.
assert_eq!(std::mem::size_of::<Marker>(), 0);Tuple Structs
Section titled “Tuple Structs”Tuple structs are named tuples. Each field is unnamed but accessible by index:
struct Point(f64, f64);
impl Point { fn distance_from_origin(&self) -> f64 { (self.0 * self.0 + self.1 * self.1).sqrt() }}
let p = Point(3.0, 4.0);assert_eq!(p.distance_from_origin(), 5.0);Tuple structs with a single field implement the newtype pattern, creating a distinct type from the wrapped type:
struct UserId(u64);struct OrderId(u64);
fn get_user(id: UserId) { /* ... */ }
let uid = UserId(42);let oid = OrderId(99);get_user(uid); // OK// get_user(oid); // ERROR: expected UserId, found OrderIdThis is type-safe and zero-cost, the compiler eliminates the wrapper at optimization time.
Named-Field Structs
Section titled “Named-Field Structs”The most common form of struct definition:
struct Person { name: String, age: u32, email: Option<String>,}
let alice = Person { name: String::from("Alice"), age: 30, email: Some(String::from("alice@example.com")),};
println!("{} is {}", alice.name, alice.age);Field Init Shorthand
Section titled “Field Init Shorthand”When the variable name matches the field name, you can use the shorthand:
fn make_person(name: String, age: u32) -> Person { Person { name, age, email: None }}Struct Update Syntax
Section titled “Struct Update Syntax”Create a new struct from an existing one, overriding specific fields:
let bob = Person { name: String::from("Bob"), ..alice // remaining fields copied from alice};// bob.name == "Bob", bob.age == 30, bob.email == Some("alice@example.com")Struct update syntax moves the remaining fields. After ..aliceThe alice binding can no longer Be used in its entirety (it is partially moved), but individual Copy fields remain accessible.
Struct Layout and #[repr]
Section titled “Struct Layout and #[repr]”By default, the compiler is free to reorder fields and add padding for alignment. The #[repr] Attribute controls the memory layout:
#[repr(C)] // C-compatible layout, fields in declaration order, C alignment rulesstruct Color { r: u8, g: u8, b: u8,}
#[repr(transparent)] // has the same layout as the single field insidestruct Wrapper(u32);
#[repr(packed)] // no padding, fields are packed tightly (may cause unaligned access)struct Packed { a: u8, b: u32, // at offset 1, not offset 4, misaligned on most platforms}
#[repr(align(16))] // forced alignment of 16 bytesstruct Aligned { data: [u8; 32],}Intuition
Section titled “Intuition”Structs group related data under named fields, while enums represent variants where exactly one variant is active at a time. Rust enums are algebraic data types: each variant can carry different data, enabling pattern matching that the compiler verifies for exhaustiveness. Structs are value types that move on assignment unless they implement Copy. Methods are defined in impl blocks, and associated functions (like constructors) are called with :: syntax.
Cross-References
Section titled “Cross-References”- [[rust/02-ownership-borrowing/ownership]] - Value types and move semantics
- [[rust/04-error-handling/error-handling]] - Result and Option enums
- [[rust/05-traits-generics/traits-and-generics]] - Trait implementations for custom types
- [[rust/03-structs-enums/advanced-patterns]] - Pattern matching and destructuring