3

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?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
user14717
  • 4,757
  • 2
  • 44
  • 68
  • 1
    [Rust generics syntax for mean function](https://stackoverflow.com/q/54168352/155423); [Implementing mean function for generic types](https://stackoverflow.com/q/34247038/155423); [How to define sum over vectors (or iterators) in a generic way?](https://stackoverflow.com/q/29459738/155423) – Shepmaster Sep 29 '20 at 16:30
  • 1
    [Why are len() and is_empty() not defined in a trait?](https://stackoverflow.com/q/60449514/155423); [Is there a subtrait of `Index` that specifies the `len` method?](https://stackoverflow.com/q/40532847/155423) – Shepmaster Sep 29 '20 at 16:33

4 Answers4

2

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.

Aplet123
  • 33,825
  • 1
  • 29
  • 55
  • 2
    You can also use [`ExactSizeIterator`](https://doc.rust-lang.org/std/iter/trait.ExactSizeIterator.html), which defines the `len` method. The values returned by `[T]::iter` and `VecDeque::iter` (and the `IntoIterator` impls on `Vec` and `VecDeque`) all implement this trait. – EvilTak Sep 29 '20 at 17:02
  • [Here's one way to write `mean` using `IntoIterator` and `ExactSizeIterator` so it works for `&[f64]`, `&Vec` and `&VecDeque`, as well as a bunch of other possible types.](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=7f91e873474cca4d6587ed1cb216be41) – trent Sep 30 '20 at 13:56
  • On reflection, you don't even need `ExactSizeIterator` because you could just `.inspect(|_| n += 1)` the iterator as you sum over it. But that might not work in the general case. – trent Sep 30 '20 at 13:56
2

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()));
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
0

I believe you can use Iterator as a parameter.

There is a good article about iterator usage.

MaxV
  • 2,601
  • 3
  • 18
  • 25
0

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()
}
Michael Anderson
  • 70,661
  • 7
  • 134
  • 187