5

I am trying to design a pair of traits (e.g. RowVector and ColumnVector from linear algebra) where each trait returns the other from one of its methods (e.g. transpose). I want to be able to add implementations of either trait in the future (such as dense and sparse vector implementations).

#[macro_use]
extern crate derive_new;

trait RowVector<Element> {
    fn transpose(self) -> ColumnVector<Element>;
}

trait ColumnVector<Element> {
    fn transpose(self) -> RowVector<Element>;
}

#[derive(new, Debug)]
struct VecRowVector<Element> {
    vec: Vec<Element>
}

#[derive(new, Debug)]
struct VecColumnVector<Element> {
    vec: Vec<Element>
}

impl<Element> RowVector<Element> for VecRowVector<Element> {
    fn transpose(self) -> VecColumnVector<Element> {
        VecColumnVector::new(self.vec)
    }
}

impl<Element> ColumnVector<Element> for VecColumnVector<Element> {
    fn transpose(self) -> VecRowVector<Element> {
        VecRowVector::new(self.vec)
    }
}

fn main() {
    let row_vector = VecRowVector::new(vec![1,2,3]);
    let col_vector = VecColumnVector::new(vec![1,2,3]);
    println!("{:?}", row_vector.transpose());
    println!("{:?}", col_vector.transpose());
}

I get an error saying that VecColumnVector is not a ColumnVector and it's expecting a 'static value.

error[E0053]: method `transpose` has an incompatible type for trait
  --> src\main.rs:22:31
   |
4  |         fn transpose(self) -> ColumnVector<Element>;
   |                               --------------------- type in trait
...
22 |         fn transpose(self) -> VecColumnVector<Element> {
   |                               ^^^^^^^^^^^^^^^^^^^^^^^^ expected trait ColumnVector, found struct `VecColumnVector`
   |
   = note: expected type `fn(VecRowVector<Element>) -> ColumnVector<Element> + 'static`
   = note:    found type `fn(VecRowVector<Element>) -> VecColumnVector<Element>`

Have I not made VecColumnVector a subtype of ColumnVector? Or do I somehow need to tell the trait that it doesn't need to be a static lifetime?

2 Answers 2

5

You are trying to return a trait. While this is possible using a trait object, it probably isn't what you want to do. A better design would be to introduce a Transpose trait, which you can model in a similar way to Rust's built-in From and Into conversion traits.

trait Transpose<To> {
    fn transpose(self) -> To;
}

impl<Element> Transpose<VecColumnVector<Element>> for VecRowVector<Element> {
    fn transpose(self) -> VecColumnVector<Element> {
        VecColumnVector::new(self.vec)
    }
}

impl<Element> Transpose<VecRowVector<Element>> for VecColumnVector<Element> {
    fn transpose(self) -> VecRowVector<Element> {
        VecRowVector::new(self.vec)
    }
}
Sign up to request clarification or add additional context in comments.

4 Comments

How do I update the RowVector and ColumnVector traits to indicate that every struct that implements them must also implement the corresponding Transpose?
I think that will get quite complicated: there will be cyclic references between constraints on each of the traits, because you will have to define the impls in strict pairs, which is hard to express in Rust. A much simpler (and more flexible) design is to not enforce any constraints on the types themselves, but rather to put constraints on the places where the types are used.
So I couldn't write functions like f<Element>(r: &RowVector<Element>, c: &ColumnVector<Element>) and expect to be able use transpose in the body? I have to list Transpose along with the other linear algebra operations that I will need?
I'm sure there is a way, but I can't directly see it, and it will probably take a bit of restructuring first. It feels like you are trying to shoehorn a Java-like paradigm - using traits in place of interfaces - and this doesn't really fit with how people program in Rust.
2

When two types need to be related, the best solution is often associated types. This precludes using dynamic dispatch like a trait object, but dynamic dispatch is still pretty limited in Rust. Rust is more expressive with static dispatch, which associated types leverage.

pub trait RowVector<Element>: Sized {
    type Transpose: ColumnVector<Element>;

    fn transpose(self) -> Self::Transpose;
}

pub trait ColumnVector<Element>: Sized {
    type Transpose: RowVector<Element>;

    fn transpose(self) -> Self::Transpose;
}

pub struct VecRowVector<Element> {
    pub vec: Vec<Element>
}

pub struct VecColumnVector<Element> {
    pub vec: Vec<Element>
}

impl<Element> RowVector<Element> for VecRowVector<Element> {
    type Transpose = VecColumnVector<Element>;

    fn transpose(self) -> VecColumnVector<Element> {
        VecColumnVector { vec: self.vec }
    }
}

impl<E: Debug> ColumnVector<Element> for VecColumnVector<Element> {
    type Transpose = VecRowVector<Element>;

    fn transpose(self) -> VecRowVector<Element> {
        VecRowVector { vec: self.vec }
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.