5
votes

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

2 Answers

4
votes

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)
    }
}
2
votes

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 }
    }
}