Unlocking the Power of Structs in Rust
What are Structs?
In the world of object-oriented programming, understanding the concept of structs is crucial for any developer. While languages like C, Go, and Rust don’t support classes, they use structs to define a group of properties instead. But what exactly are structs, and how do they operate in Rust?
Rust 101
Rust, a programming language created by Mozilla, is a fast, low-level language that uses modern syntax patterns and a central package manager. It’s similar to C, but with a more modern twist. In Rust, structs are used to define a group of properties, but they don’t allow you to define methods.
Writing a Struct in Rust
Let’s dive into an example of writing a simple struct for a Cat type that includes the properties name and age. We’ll define our struct, create a new instance of it, and print the entire struct using the {:?}
formatter.
“`rust
struct Cat {
name: String,
age: u8,
}
fn main() {
let cat = Cat {
name: String::from(“Scratchy”),
age: 4,
};
println!(“{:?}”, cat);
}
“`
The Derive Attribute
By default, structs aren’t printable. To use the {:?}
formatter with println!
, we need to implement the std::fmt::Debug
trait. But instead of doing it manually, we can use the derive(Debug)
attribute to automate the implementation of certain traits on our struct.
Struct Traits
In Rust, traits define a bundle of functions for structs to implement. We can create a struct with properties, but how can we tie them to functions like we do with classes in other languages? The answer lies in traits.
Let’s define a trait called Pet
that includes the functions birthday
and sound
. We’ll then implement this trait on our Cat
and Dog
structs.
“`rust
trait Pet {
fn birthday(&mut self);
fn sound(&self) -> String;
}
struct Cat {
name: String,
age: u8,
}
impl Pet for Cat {
fn birthday(&mut self) {
self.age += 1;
}
fn sound(&self) -> String {
String::from(“Meow”)
}
}
struct Dog {
name: String,
age: u8,
}
impl Pet for Dog {
fn birthday(&mut self) {
self.age += 1;
}
fn sound(&self) -> String {
String::from(“Ruff”)
}
}
“`
Returning a Struct
Sometimes, a function may return several possible structs that implement the same trait. To write this type of function, we can use the Box
type to allocate enough memory for any struct implementing the Pet
trait.
rust
fn new_pet(name: String) -> Box<dyn Pet> {
if name == "Scratchy" {
Box::new(Cat { name, age: 0 })
} else {
Box::new(Dog { name, age: 0 })
}
}
Summary
In this article, we’ve learned the following about structs in Rust:
- Structs allow us to group properties in a single data structure
- Using traits, we can implement different methods on a struct
- Typing with traits allows us to write functions that can receive and return structs
- The derive attribute allows us to implement certain traits in our structs with ease
With these concepts under our belt, we can now implement typical object-oriented design patterns in Rust using composition over inheritance.