Add Behavior for packed data: rust

Binding Behavior to Structs in Rust

Amay B
CoderHack.com
3 min readSep 26, 2023

--

Photo by Sebastian Herrmann on Unsplash

Rust enables you to add behavior to structs using impl blocks. An impl block contains methods for a struct. Let’s look at an example:

struct Point {
x: i32,
y: i32,
}

impl Point {
fn x(&self) -> i32 {
self.x
}

fn y(&self) -> i32 {
self.y
}
}

fn main() {
let origin = Point { x: 0, y: 0 };

println!("The x coordinate is {}", origin.x());
println!("The y coordinate is {}", origin.y());
}

Here we have a Point struct with x and y fields. We then have an impl block which contains two methods, x and y, which return the x and y coordinates respectively. We can call these methods on instances of the Point struct, like we do in the main function.

Methods take self as their first parameter which is a reference to the struct the method is being called on. The &self syntax means the method takes an immutable borrow of the struct.

Using Methods

Here’s another example of a struct with methods:

struct Rectangle {
length: u32,
width: u32,
}

impl Rectangle {
fn area(&self) -> u32 {
self.length * self.width
}
}

fn main() {
let rect = Rectangle { length: 30, width: 50 };

println!(
"The area of the rectangle is {} square pixels.",
rect.area()
);
}

Here we have a Rectangle struct with length and width fields. We implement a method area which returns the area of the rectangle by multiplying the length and width. We can call rect.area() and pass a Rectangle instance to get its area.

Methods allow us to abstract away complex logic behind a simple interface. The area method hides the multiplication calculation behind a simple method call.

Associated Functions

We can also have associated functions on a struct which are defined in an impl block but take the struct as an argument instead of self. This is similar to static functions in Java or C++ in some sense. Here’s an example:

struct Rectangle {
length: u32,
width: u32,
}

impl Rectangle {
fn square(size: u32) -> Rectangle {
Rectangle { length: size, width: size }
}
}

fn main() {
let sq = Rectangle::square(3);
println!(
"The area of the square is {}",
sq.length * sq.width
);
}

Here we have an associated function square which takes a size and returns a Rectangle struct where length and width are both equal to the passed in size. We call it with Rectangle::square(3) and it returns a square Rectangle struct with sides of length 3.

Associated functions are useful for constructors or factory methods. Here we use one to create a square Rectangle.

You can have multiple impl blocks for a single struct. For example:

struct Point {
x: i32,
y: i32,
}

impl Point {
fn x(&self) -> i32 {
self.x
}
}

impl Point {
fn y(&self) -> i32 {
self.y
}
}

fn main() {
let origin = Point { x: 0, y: 0 };

println!("The x coordinate is {}", origin.x());
println!("The y coordinate is {}", origin.y());
}

Here we split the methods for Point into two separate impl blocks. This is useful to visually separate concerns, but has no other effect - we can still call both methods on a Point.

Here’s another example with multiple impl blocks:

struct Rectangle {
width: i32,
height: i32
}

impl Rectangle {
fn area(&self) -> i32 {
self.width * self.height
}
}

impl Rectangle {
fn perimeter(&self) -> i32 {
2 * self.width + 2 * self.height
}
}

fn main() {
let rect = Rectangle { width: 10, height: 5 };

println!("The area is: {}", rect.area());
println!("The perimeter is: {}", rect.perimeter());
}

Here we have two impl blocks with different methods for our Rectangle struct. One calculates the area while the other calculates the perimeter. By splitting them into two impl blocks we can visually separate these two concerns, even though both impl blocks operate on the same struct.

Conclusion

We’ve covered how to add methods and associated functions to Rust structs using impl blocks. Methods allow you to abstract logic behind an interface and associated functions act as constructors. You can also have multiple impl blocks for the same struct to visually separate concerns.

Hope this helps give you an overview of how to bind behavior to structs in Rust! Let me know if you have any other questions.

--

--