Structs and Enums
Structs can contain fields that hold enums, and enums can define variants that hold structs. Using structs and enums together can satisfy a wide array of data structure requirements.
impl blocks
Types like structs or enums can have functions associated with them using impl blocks. For example, we can define a function that:
- takes a shared reference to have the animal say its name
- takes an exclusive reference to mutate the values, dying the color of the animal
- takes ownership, and doesn’t return the value, thus causing it to drop and be inaccessible afterwards
These functions use &self, &mut self, and self, which all refer to the type that the function is being implement for.
fn main() {
let mut animal = Animal {
name: "Vincent Van Dog".to_string(),
legs: 4,
color: "brown".to_string(),
};
animal.say_name();
animal.dye_animal();
dbg!(&animal);
animal.release_to_wild();
// can no longer access animal, because it
// was moved and dropped
// dbg!(animal);
}
#[derive(Debug)]
struct Animal {
name: String,
legs: u32,
color: String,
}
impl Animal {
fn say_name(&self) {
println!("My name is {}", self.name);
}
fn dye_animal(&mut self) {
self.color = "pink".to_string();
}
fn release_to_wild(self) {}
}
Owned data is easier to work with
When working with structs you can choose to define structs using owned data like String or references like &str. When you’re just starting out, prefer owned data almost always. This makes it easier to judge which variable actually owns the relevant data.
Using references in structs can be useful, especially for zero-allocation parsing and other use cases. References in struct definitions also requires more understanding of which variable owns the data and how long references live for.