Is there a trait that works for both VecDeque and Vec?

951 Views Asked by At

We can pass a Vec<f64> to this function, but we cannot pass a VecDeque, because that is two slices:

fn mean(v: &[f64]) -> f64 {
    let sum = v.iter().sum();
    sum / v.len() as f64
}

Is there a trait, something perhaps analogous to a C++ RandomAccessContainer, that allows us to generically write code that operates on both Vec and VecDeque?

4

There are 4 best solutions below

0
On

I believe you can use Iterator as a parameter.

There is a good article about iterator usage.

3
On

You can just chain traits, like Index<usize, Output = f64> to get elements at an index, and IntoIterator<Item = &f64> to use IntoIterator. If you need len, then you can make your own trait and impl it.

0
On

There is an alternate approach that is worth considering in these cases, rather than looking for a trait that both types A and B implement that you can pass into to your function, you create a new trait that both support, and use your new trait instead of the function. It usually means a little bit of duplication and boilerplate, but it is nicely extensible later.

trait HasMeanF64 {
    fn mean(&self) -> f64;
}

impl HasMeanF64 for &[f64] {
    fn mean(&self) -> f64 {
        self.iter().sum::<f64>() / self.len() as f64
    }
}

impl HasMeanF64 for Vec<f64> {
    fn mean(&self) -> f64 {
        self.iter().sum::<f64>() / self.len() as f64
    }
}

impl HasMeanF64 for VecDeque<f64> {
    fn mean(&self) -> f64 {
        self.iter().sum::<f64>() / self.len() as f64
    }
}

Now instead of writing mean(v) you can write v.mean().

And if you still want your mean(v) form you can write

fn mean<T: HasMeanF64>(v: &T) -> f64 {
    v.mean()
}
0
On

There are 2 approaches:

  1. Make your function accept a slice of slices: fn mean(&[&[f64]]) -> f64. Can be called like mean(&[v.as_slice()])
  2. Make it accept Iterator. I think this it is most acceptable way to allow use both VecDeque and Vec:
use std::collections::VecDeque;
use std::iter::Iterator;
use std::vec::Vec;

fn mean<T: Iterator<Item = f64>>(iter: T) -> f64 {
    let (last_index, sum) = iter
        .enumerate()
        .fold((0, 0.0), |(_, sum), (i, v)| (i, sum + v));
    sum / (last_index as f64 + 1.0)
}

fn main() {
    let v: Vec<f64> = vec![0.0, 1.0, 2.0, 3.0, 4.0];
    println!("{:?}", mean(v.iter().copied()));
    let v: VecDeque<f64> = v.into();
    println!("{:?}", mean(v.iter().copied()));
}