Common type for &Vec<T> or &Vec<&T> without code change

In some situations, I have &Vec that is forward in chain way too many places and functions. At some point I decide that I need a extra filtering, using iter().filter().collect() will generate a Vec<&T>. This forces me now to start to change every following method into &Vec<&T>.

While I understand that for the compiler &Vec and &Vec<&T>, for most of the code, it doesn't matter, both are read-only things.

Obviously I can always send as &Vec<&T>. But it is a bit ugly since only 1/5 of times I create something like it I really need to convert to refs.

Iterators are not a good option since I need to re-read those collections multiple times. Enum with both types and a common trait solve it, but I would prefer a more "native language" approach.

Any tips on how I can deal with this kind of situation more flexible?

For instance

fn main() {
   let users = vec![User("one"), User("two"), User("three")];
   f1(&users)
}

fn f1(users: &Vec<User>) {
  // do stuff
  f2(users);
}

fn f2(users: &Vec<User>) {
  // do stuff
  let users = users.iter().filter(|user| !user.name.is_empty()).collect()
  f3(users);
}

fn f3(users: &Vec<&User>) {
  // do stuff
  f4(users);
}

fn f4(users: &Vec<&User>) {
  // we are done
}

The type you are looking for is &[T] which is a borrowed slice. &Vec<T> coerces to &[T] because Vec implements Deref<Target=[T]>, so you can write code like this: playground.

I also derive Copy for the User type so the collected vector can own its items and avoid the vector of borrowed types. This may not be possible for all types, but it's nice when you have a Copy type!

See also the chapter on slices in The Book.

e: Another interesting trick, especially if your type is not Copy, is to implement AsRef and use generics and trait bounds. This method does add extra boilerplate to your function signatures, though: playground

3 Likes

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.