Writing Reusable Code

Although there are plenty of good ones already out there, I started working on a 3D math library as an exercise to jump into Rust called vex. My code is becoming redundant, and I’m not sure on how to go about breaking out the reusable pieces. For example, all three of my vector classes (Vector2, Vector3, Vector4) have a lot of copy and paste code between them. For example, this equality trait implementation is identical across all three classes:

impl cmp::PartialEq for Vector2 {
    fn eq(&self, _rhs: &Vector2) -> bool {
        for i in 0..2 {
            if self[i] != _rhs[i] {
                return false;
            }
        }

        true
    }
}

I thought about making an internal vector module contains a bunch of common functionality. Then, each pub Vector type would import that internal module, and then, for example, all VectorXs’ operator implementations would just be wrappers for those internal function calls. Does that sound like a good approach? I also thought about implementing traits with default implementation functions making them act like base classes that one would find in an OO language, but I’m not sure if that makes sense, or is even possible since the bulk of these classes involve implementing operator trait implementations as it is.

Is it possible to create a trait that implements multiple other traits? If so, is that even a good idea?

Another question I had was documentation. At first I thought it might be a good rule of thumb to document all public functions, but now I’m thinking that it’s a bit too much. Maybe I shouldn’t have documentation for all vector and matrix operator implementations. Hopefully, I’ve made them obvious enough to where I can just make unit tests in a sub-module at the bottom of the source file for them instead. Thoughts?

1 Like

One way I use to reduce similar code clones in my own code is private, module-level functions (this is assuming Vector2, Vector3, and Vector4 all live under the same module. Even if they live in separate submodules with a common ancestor module, this still works).
That way, all types can access the fn in their impls, but the fn stays an implementation detail.

I also did something similar, as a way to learn Rust. I would look into macros and the crate typenum and generic-array. Macros can allow you to implement the operators with little code duplication. Typenum can help you get type level integers. Generic-array uses those to get a generalized array that lives on the stack. Or you could use a Vec to back the vectors instead, that may be easier to implement.

1 Like

If you have code that is shared across different struct impls, then you can create a trait with that code as a default implementation. Then implement the trait for each of the structures. If you need any change, you can override the default implementation.

The only downside is that it is more difficult to discover code as you hav eto search for the trait implementation and have to remember to import the trait every time you use it outside the module.

Does this helps? https://play.rust-lang.org/?gist=e15c0d2ecd3cb6b66fb4d4902ee856e3&version=stable&mode=debug&edition=2015

You first create a trait for the vector, the you define all the same code based on the trait definition.

So I guess that the trait vector could have a method components that returns all the components of the vector in order.

Great, now you can define your equality on top of that. I guess, let me try…