Custom traits on default types
Published on 20180225
3 min read
In category
rust
Custom traits on default types
Recently, I started writing a library for physics calculations in, you guessed it, Rust. Strangely, there aren't many available. At least none of the libs that I've found are dedicated purely to physics. Most of them are geared towards gaming engines. That's fine and all, but still...
It's a nascent project so it's going through its growing pains, but I have high hopes for it. I miss physics anyway so this is a good excuse to relearn what I haven't touched in a few years. The problem right now, as it turns out, is with vectors. Right now the kinematics section of the lib is being developed (albeit slowly), and rather than play with scalar calculations, it'd be nice if I could use vectors since they're so much more descriptive of a given problem. Specifically, with vectors, I'll be needing the dot product (and curl soon enough) but it doesn't exist with Rust's native Vector type. I could introduce one of the many linear algebra crates to my project, but I'd like to keep it as lean as possible. I may resort to one in the future, but right now I don't see the necessity. I could also write a function which takes two vectors and returns the dot product:
fn dot_product(v1: Vector<f64>, v2: Vector<f64>) > f64 {
assert!(
v1.len() == v2.len(),
"Vectors are not of equal length!"
);
let mut dot_product = 0.0;
for i in 0..v1.len() {
let item_v1: f64 = v1[i];
let item_v2: f64 = v2[i];
dot_product += item_v1 * item_v2;
}
dot_product
}
That works. But, I'd like the implementation to be cleaner. So my other strategy is to append a new trait Dot
to my vectors so that I can call v1.dot(v2)
and get what I want. How would I do that if Rust already has ownership of the Vector type?
Easy. I need to implement a new trait onto the Vector type and have it available to any given scope where I may want to use it. Let's see how to do that...
Because I want to be able to use this trait wherever I want (and not redefine it in every file) it makes sense to create a utility file where this trait will live. Ignoring that part, the implementation is as such:
pub trait Dot<T> {
fn dot(self, v2: Vec<T>) > T where T: Float;
}
impl<T> Dot<T> for Vec<T> {
fn dot(self, v2: Vec<T>) > T
where T: Float
{
assert!(self.len() == v2.len(),
"Vectors are not of equal length"
);
let mut dot_product: f64 = 0.0;
for i in 0..self.len() {
let item_from_v1: f64 = num::cast(self[i]).unwrap();
let item_from_v2: f64 = num::cast(v2[i]).unwrap();
dot_product += item_from_v1 * item_from_v2;
}
num::cast(x).unwrap()
}
}
And to use this I need to do two things:

The module in which I defined my trait needs to be declared in my root (lib.rs) file, e.g.
mod utils;

The trait itself needs to declared in the scope (file) I want to use it in, e.g.
use utils::{Dot};
Now, any time I use a standard Rust Vector type I have the .dot()
available to me.
let v1 = vec![1.0, 2.0, 3.0];
let v2 = vec![0.0, 1.0, 2.0];
let dot_product = v1.dot(v2); // 8.0
Very clean.