Structs and Enums
Define custom types to model your domain.
§Structs
Struct Definition
struct User {
name: String,
email: String,
age: u32,
active: bool,
}
Creating Instances
let user = User {
name: String::from("Alice"),
email: String::from("alice@example.com"),
age: 30,
active: true,
};
println!("{}", user.name);
Field Init Shorthand
let name = String::from("Bob");
let email = String::from("bob@example.com");
let user = User {
name,
email,
age: 25,
active: false,
};
Struct Update Syntax
let user2 = User {
email: String::from("bob2@example.com"),
..user
}; // copies other fields from user
§Tuple Structs
Lightweight structs with unnamed fields:
struct Point(i32, i32, i32);
let origin = Point(0, 0, 0);
println!("{}", origin.0); // 0
§Unit Structs
Structs with no fields (used as markers):
struct Marker;
let m = Marker;
§Impl Blocks
Add methods to structs:
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.height > other.height
}
fn square(size: u32) -> Rectangle {
Rectangle { width: size, height: size }
}
}
let rect = Rectangle { width: 30, height: 50 };
println!("Area: {}", rect.area());
let square = Rectangle::square(10);
"Methods on structs make data and behavior live together. This is the essence of object-oriented design in Rust."
§Enums
Define a type with multiple possible variants:
enum IpAddrKind {
V4,
V6,
}
let four = IpAddrKind::V4;
let six = IpAddrKind::V6;
Enums with Data
enum IpAddr {
V4(String),
V6(String),
}
let localhost = IpAddr::V4(String::from("127.0.0.1"));
let ipv6 = IpAddr::V6(String::from("::1"));
Different data per variant:
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
Impl on Enums
impl Message {
fn call(&self) {
// method body
}
}
let m = Message::Write(String::from("Hello"));
m.call();
§Option Enum
Built-in enum for values that may or may not exist:
enum Option<T> {
Some(T),
None,
}
let some_number = Some(5);
let some_string = Some("a string");
let absent_number: Option<i32> = None;
Match on Option:
fn plus_one(x: Option<i32>) -> Option<i32> {
match x {
None => None,
Some(i) => Some(i + 1),
}
}
let five = Some(5);
let six = plus_one(five);
let none = plus_one(None);
§Result Enum
Built-in enum for fallible operations:
enum Result<T, E> {
Ok(T),
Err(E),
}
§Generic Structs and Enums
Create flexible types:
struct Point<T> {
x: T,
y: T,
}
let int_point = Point { x: 5, y: 10 };
let float_point = Point { x: 1.0, y: 4.0 };
Multiple type parameters:
struct Pair<T, U> {
first: T,
second: U,
}
let pair = Pair {
first: 5,
second: "hello",
};
Generic enums:
enum Result<T, E> {
Ok(T),
Err(E),
}
§Derive Traits
Auto-implement common traits:
#[derive(Debug, Clone, Copy)]
struct Point {
x: i32,
y: i32,
}
let p = Point { x: 1, y: 2 };
println!("{:?}", p); // works because of Debug
Common derive traits:
Debug- print with{:?}Clone- explicit copyingCopy- implicit copying for small typesPartialEq- equality comparisonEq- equality (reflexive)PartialOrd- orderingOrd- total orderingHash- hashableDefault- default values
§Practice
- Define a struct representing a person with methods
- Create an enum for different payment methods
- Implement a method on your enum
- Use generic structs to store different types
- Derive Debug and print a struct
- Use Option to represent optional fields
- Match exhaustively on enum variants
"Structs and enums are your tools for domain modeling. Build your types carefully, and let the compiler guide you."