简体   繁体   中英

How might I return a reference to a new collection inside of a closure in Rust?

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?

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
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM