Augmenting Existing Struct APIs with Rust Traits
What I'm going to describe is just derived from how traits themselves work, but despite having now written a decent amount of Rust, I had not really fully absorbed this fact.
Imagine you are working with some cell towers owned by various people, in various locations:
struct CellTower {
id: String;
location: Location;
owner_id: String;
}
You might want to do various things with these so you can add an impl
block with some helper functions to help make your code legible.
impl CellTower {
fn distance_from(&self, point: Location): Distance {
return self.location.distance(point)
}
}
This lets you do stuff like cell_tower.distance_from(my_house)
and other "normal" programming with objects that we're all used to. It is not really different
from something like
distance_from(cell_tower, my_house)
But aesthetics really do matter.
You heard about fluent interfaces and want that! You want nice method chaining.
In Python and friends, my strategy might be to create a wrapped container class and build an interface with that.
But here with Rust we actually can totally circumvent this and add extra functionality to existing data structures:
trait FilterableTowerContainer {
fn owned_by(self, owner_id: String) -> Self
fn close_to(self, location: Location) -> Self
}
impl FilterableTowerContainer for Vec<CellTower> {
fn owned_by(self, owner_id: String) -> Self {
...
}
fn close_to(self, location: Location) -> Self {
...
}
}
fn main() {
let towers : Vec<CellTower> = ...;
let my_home: Location = ...;
let my_favorite_company = ...;
let usable_towers = towers
.close_to(my_home)
.owned_by(my_favorite_company);
println!(
"Number of found usable towers: {}",
usable_towers.len()
);
}
The interesting bit here being:
let usable_towers = towers
.close_to(my_home)
.owned_by(my_favorite_company);
Despite me not having written Vec
's implementation, I'm able to "add more methods" to it.
This is all just really how all traits work, but if you're coming from a "objects are instances of classes and have a specificied MRO" world it's very magical!
And it saves you from writing things like:
let usable_towers = owned_by(
close_to(my_towers, my_home),
my_favorite_company
)
I'll leave more cursed trait implementation ideas as an exercise to the reader.
Update: the term of art for this in Rust is Extension Traits.