Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can you easily borrow a Vec<Vec<T>> as a &[&[T]]?

Tags:

rust

How can you easily borrow a vector of vectors as a slice of slices?

fn use_slice_of_slices<T>(slice_of_slices: &[&[T]]) {
    // Do something...
}

fn main() {
    let vec_of_vec = vec![vec![0]; 10];
    use_slice_of_slices(&vec_of_vec);
}

I will get the following error:

error[E0308]: mismatched types
 --> src/main.rs:7:25
  |
7 |     use_slice_of_slices(&vec_of_vec);
  |                         ^^^^^^^^^^^ expected slice, found struct `std::vec::Vec`
  |
  = note: expected type `&[&[_]]`
             found type `&std::vec::Vec<std::vec::Vec<{integer}>>`

I could just as easily define use_slice_of_slices as

fn use_slice_of_slices<T>(slice_of_slices: &[Vec<T>]) {
    // Do something
}

and the outer vector would be borrowed as a slice and all would work. But what if, just for the sake of argument, I want to borrow it as a slice of slices?

Assuming automatic coercing from &Vec<Vec<T>> to &[&[T]] is not possible, then how can I define a function borrow_vec_of_vec as below?

fn borrow_vec_of_vec<'a, T: 'a>(vec_of_vec: Vec<Vec<T>>) -> &'a [&'a [T]] {
    // Borrow vec_of_vec...
}

To put it in another way, how could I implement Borrow<[&[T]]> for Vec<Vec<T>>?

like image 721
Olivier Avatar asked Apr 27 '18 06:04

Olivier


People also ask

How do you pass a vector reference in Rust?

Use primeFactors. as_slice() (or primeFactors. as_mut_slice() if you want to modify the contents of the vector in place) to pass a vector as a borrowed pointer. Change your function to accept a borrowed slice of type &[T] .

What is a VEC in Rust?

Vector is a module in Rust that provides the container space to store values. It is a contiguous resizable array type, with heap-allocated contents. It is denoted by Vec<T>. Vectors in Rust have O(1) indexing and push and pop operations in vector also take O(1) complexity.

How do you access vector elements in Rust?

How to access vector values. Rust provides us with two methods to access elements in a vector. We can access values with either the indexer, or with the get() method.

How do you initialize VEC in Rust?

In Rust, there are several ways to initialize a vector. In order to initialize a vector via the new() method call, we use the double colon operator: let mut vec = Vec::new();


2 Answers

You cannot.

By definition, a slice is a view on an existing collection of element. It cannot conjure up new elements, or new views of existing elements, out of thin air.

This stems from the fact that Rust generic parameters are generally invariants. That is, while a &Vec<T> can be converted as a &[T] after a fashion, the T in those two expressions MUST match.


A possible work-around is to go generic yourself.

use std::fmt::Debug;

fn use_slice_of_slices<U, T>(slice_of_slices: &[U])
where
    U: AsRef<[T]>,
    T: Debug,
{
    for slice in slice_of_slices {
        println!("{:?}", slice.as_ref());
    }
}

fn main() {
    let vec_of_vec = vec![vec![0]; 10];
    use_slice_of_slices(&vec_of_vec);
}

Instead of imposing what the type of the element should be, you instead accept any type... but place a bound that it must be coercible to [T].

This has nearly the same effect, as then the generic function can only manipulate [T] as a slice. As a bonus, it works with multiple types (any which can be coerced into a [T]).

like image 114
Matthieu M. Avatar answered Oct 18 '22 00:10

Matthieu M.


A deref coercion from Vec<T> to &[T] is cheap. A Vec<T> is represented by a struct essentially containing a pointer to the heap-allocated data, the capacity of the heap allocation and the current length of the vector. A slice &[T] is a fat pointer consisting of a pointer to the data and the length of the slice. The conversion from Vec<T> to &[T] essentially requires to copy the pointer and the length from the Vec<T> struct to a new fat pointer.

If we want to convert from Vec<Vec<T>> to &[&[T]], we need to perform the above conversion for each of the inner vectors. This means we need to store an unknown number of fat pointers somewhere. This requires to allocate space for these fat pointers somewhere. When converting a single vector, the compiler will reserve space for the single resulting fat pointer on the stack. For an unknown, potentially large, number of fat pointers this is not possible, and the conversion also isn't cheap anymore. This is the reason this conversion isn't easily possible, and you need to write explicit code for it.

So whenever you can, you should instead change your function signature as suggested in Matthieu's answer. If you don't control the function signature, your only choice is to write the explicit conversion code, allocating a new vector:

fn vecs_to_slices<T>(vecs: &[Vec<T>]) -> Vec<&[T]> {
    vecs.iter().map(Vec::as_slice).collect()
}

Applied to the functions in the original post, this can be used like this:

use_slice_of_slices(&vecs_to_slice(&vec_of_vec));
like image 2
Sven Marnach Avatar answered Oct 18 '22 00:10

Sven Marnach