Richard
Richard

Reputation: 424

Convert Vec<&T> to &[T] without allocation

Problem Statement

I am trying to call fn some_api_function() which takes &[T] as parameter. To generate that parameter for the function, I tried to call flat_map on a Vec of Vecs (which itself buried inside RefCell). But I have trouble convert Vec<&T> to &[T]. I'd preferably avoid Copy or Clone the entire dataset for performance reason, as some_api just need to read-only borrow.

Code to illustrate:

use std::cell::RefCell;
pub struct EnvVar {}

pub struct Arena {
    services: RefCell<Vec<Service>>,
}

pub struct Service {
    env_vars: Vec<EnvVar>,
}

pub fn some_api(env_vars: &[EnvVar]) {}

fn main() {
    let arena = Arena {
        services: RefCell::new(vec![Service {
            env_vars: vec![EnvVar {}],
        }]),
    };
    let env_vars: Vec<&EnvVar> = arena
        .services
        .borrow()
        .iter()
        .flat_map(|compose_service| compose_service.env_vars.as_ref())
        .collect();

    some_api(&env_vars);
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types
  --> src/main.rs:27:14
   |
27 |     some_api(&env_vars);
   |              ^^^^^^^^^ expected slice, found struct `Vec`
   |
   = note: expected reference `&[EnvVar]`
              found reference `&Vec<&EnvVar>`

For more information about this error, try `rustc --explain E0308`.
error: could not compile `playground` due to previous error

Upvotes: 1

Views: 309

Answers (1)

John Kugelman
John Kugelman

Reputation: 361565

It's not possible, and here's why:

Imagine that each T takes up 500 bytes. Meanwhile each &T, being a 64-bit address, takes only 8 bytes. What you get is:

  • A &[T] is a reference to a slice of contiguous Ts, each 500 bytes. If the slice had 20 Ts that contiguous block of memory would be 20 × 500 bytes = 10 KB large.

  • A Vec<&T> containing 20 elements would have a bunch of addresses side-by-side and would only need a block of 20 × 8 bytes = 160 bytes.

There is no cheap way to turn a 160-byte block of &Ts into a 10 KB block of Ts. Their memory layouts are not compatible. Your only options are:

  1. Clone the objects.
  2. Change the caller to build a Vec<T> instead of Vec<&T>.
  3. Change the function to accept &[&T].

Upvotes: 6

Related Questions