1

One can pass a Vec to function expecting a slice with implicit coersion:

fn foo(arg: &[u8]) {}

foo(&vec![]); // compiles fine

Why this cannot be extended to nested slices and Vecs?

fn bar(arg: &[&[u8]]) {}

bar(&vec![&vec![]]); // Compilation error

// Can be worked around by explicit conversion to slices
bar(&vec![&vec![][..]][..]);

Example in the playground.

Jen
  • 908
  • 5
  • 21

1 Answers1

3

Because dereferencing Vec<T> (src) results in &[T], i.e. given Vec<Vec<u8>> it results in &[Vec<u8>] and not &[&[u8]].

Ultimately, you'll need to map(), deref()/as_slice() and collect() to obtain &[&[u8]]:

let v: Vec<Vec<u8>> = ...;

let v = v.iter().map(std::ops::Deref::deref).collect::<Vec<_>>();
// or
let v = v.iter().map(Vec::as_slice).collect::<Vec<_>>();

bar(&v);

Alternatively depending on the context, you could change bar() into a generic function and accept IntoIterator. Then both would be possible:

fn bar<I, T>(iter: I)
where
    I: IntoIterator<Item = T>,
    T: AsRef<[u8]>,
{
    ...
}

bar(&vec![&vec![]]);
bar(&vec![&vec![][..]][..]);

The reason &vec![&vec![]] doesn't work, is because the inner & doesn't result in the Vec<u8> to dereference into &[u8], but instead just references the Vec resulting in &Vec<u8>.

The outer & then doesn't work, because bar expects &[&[u8]]. But it receives &Vec<&Vec<u8>> and dereferencing to &[&Vec<u8>] still isn't what bar expects either. Thus why you receive the mismatched types error.

Your second example &vec![&vec![][..]][..] works, because you're explicitly causing both Vecs to be dereferenced to slices. This can however be "simplified" into something more readable using as_slice(), i.e. bar(vec![vec![].as_slice()].as_slice());. However, ultimately to get a &[&[u8]] from an arbitrary Vec<Vec<u8>> you'd need to deref and collect into a new Vec<&[u8]>.

Related: "What are Rust's exact auto-dereferencing rules?"

vallentin
  • 19,107
  • 6
  • 43
  • 68
  • To add context to this, it arguably makes sense you need the explicit `map/collect` because creating the `&[&[u8]]` is potentially going to allocate enough memory for all the `&[u8]`s and do work for each item in the original Vec. There could be a million `&[u8]`s in that Vec; you wouldn't want to use space and do work for each item implicitly. – twotwotwo Jan 02 '21 at 23:34
  • Thanks a lot for this exhaustive answer. It really helped me understand this. <3 – Jen Jan 14 '21 at 16:11