0

I am trying to use a map-like function from an external library that essentially has the following shape:

fn map_like<'a, F>(nums: &[usize], f: F) -> Vec<&'a [u8]>
where
    F: Fn(usize) -> &'a [u8],
{
    nums.iter().map(|num| f(*num)).collect()
}

I also have a constant lookup "table":

const NAMES: [&str; 4] = ["Ada", "Hal", "Max", "Val"];

and a previously generated array of indices for looking up items in the table:

let indices: &[usize] = &[0, 3, 1, 2];

I want to use the map-like function to generate an array of "greetings" (as bytes) from the lookup table and the indices.

I can generate the following trivial "greetings":

let greetings: Vec<&[u8]> = map_like(indices, &|i| {
    let name: &str = NAMES[i];
    let greeting: &str = name;
    greeting.as_bytes()
});

// Prints `[[65, 100, 97], [86, 97, 108], [72, 97, 108], [77, 97, 120]]`
println!("{:?}", greetings);

But what I really want to do is format a larger greeting out of each name:

// Does not compile
let greetings: Vec<&[u8]> = map_like(indices, &|i| {
    let name: &str = NAMES[i];
    let greeting: String = format!("Hello, {}!", name);
    greeting.as_bytes()
});

Unfortunately, the compiler doesn't allow this. It throws the following error:

error[E0515]: cannot return reference to local variable `greeting`
  --> src/main.rs:10:9
   |
10 |         greeting.as_bytes()
   |         ^^^^^^^^^^^^^^^^^^^ returns a reference to data owned by the current function

I believe that I even understand what is happening: the format! macro is creating a new String that's local to the closure, which as_bytes in turn creates a reference to. Since this new string is deallocated at the end of the closure, this would result in a dangling reference, which the compiler won't allow.

Even so, I am at a loss for a workaround. I can't come up with a reasonable method for constructing a larger greeting from each name that the compiler is happy with. What is an idiomatic Rust way of approaching this?

  • You can't return it as a reference, so you will have to return an owned value, e.g. `Vec>` – Jmb Jul 26 '22 at 20:51

1 Answers1

0

Instead of returning references, you should return String with the ownership. That is, use String instead of &[u8] like:

fn map_like<F>(nums: &[usize], f: F) -> Vec<String>
where
    F: Fn(usize) -> String,
{
    nums.iter().map(|num| f(*num)).collect()
}

let greetings: Vec<String> = map_like(indices, &|i| {
    let name: &str = NAMES[i];
    let greeting: String = format!("Hello, {}!", name);
    greeting
});
  • If I understand you correctly, it seems like the issue here is the signature of the function `map_like`. Unfortunately, I don't have much control over how that function is defined because it's from an external library. But if that's the takeaway, that's totally fine. – andrewtbiehl Jul 26 '22 at 21:07
  • Yes, that is it as far as I can think of. – Özgür Murat Sağdıçoğlu Jul 26 '22 at 21:20