Casting vectors

What is the best way to cast a vector?

let src: Vec<u8> = vec![1, 2, 3, 4, 5];

let dst: Vec<Vec<u8>> = src.chunks(2).collect::<Vec<Vec<u8>>>();
// or
let dst: Vec<Vec<u8>> = src.chunks(2).map(|s| s.into()).collect();

The first variant doesn't work, so I guess the answer is obvious?

3 Likes

Why does it not work? Because the cast is not implemented?

Well, first of all, there's no "cast" in any case. Casting in Rust is done with as keyword, and in most cases it's actually discouraged due to the possibility of unwanted implicit conversions (the only exception I know for now is creating of trait objects with as Box<dyn Trait> or similar).

In this case, there's not a cast, but simple generic call. Note that these two lines are equivalent:

let dst: T = src_iter.collect();
let dst = src_iter.collect::<T>();

since collect's signature is to return its generic parameter. Later, I'll use only the latter, where the generic parameter is set explicitly and the return type follows from it.

Now, looking at the signature again, we see that:

  • collect is a method of Iterator trait, i.e. it can be called on src_iter if src_iter: Iterator;
  • it has an additional bound on its parameter, namely B: FromIterator<Self::Item>; that is, src_iter is an iterator of some Items, and the generic parameter must know how to build itself from this iterator.

Looking at the docs for Vec<T>, we see that it implements FromIterator only with Item = T. That is, vector will not perform any implicit conversions while collecting. It technically could, i.e. this implementation might require Item: Into<T>, for example; but this would a) complicate the inference so that it might become unsolvable in some cases where it's currently solvable, and b) introduce the implicit step with unknown complexity inside fairly common operation. Both these things go against the design of Rust.

The last point is, what are the exact iterators in out case? The .chunks() method on Vec is provided by slice, and the corresponding iterator yields subslices of the original slice, so to have a Vec<Vec<_>>, you must somehow get the copy of the values in these subslices - most likely by cloning them (and that's what From, and therefore Into, does in this case).

In short: just map explicitly. This would be easier both for compiler and for future yourself.

2 Likes