Rust closures from factory functions

2019-01-29 01:40发布

问题:

I have some Rust code I'm trying to get working but I'm not sure how to go about it.

fn main() {
    let names = vec!["foo", "bar", "baz"];
    let print = printer(names);
    let result = print();
    println!("{}", result);
    do_other_thing(names.as_slice());
}

fn printer(names: Vec<&str>) -> Box<Fn() -> String> {
    Box::new(move || {
        let text = String::new();
        for name in names {
            text = text + name;
        }
        text
    })
}

fn do_other_thing(names: &[&str]) {}

This compiles with:

error[E0477]: the type `[closure@src/main.rs:10:14: 16:6 names:std::vec::Vec<&str>]` does not fulfill the required lifetime
  --> src/main.rs:10:5
   |
10 |       Box::new(move || {
   |  _____^ starting here...
11 | |         let text = String::new();
12 | |         for name in names {
13 | |             text = text + name;
14 | |         }
15 | |         text
16 | |     })
   | |______^ ...ending here
   |
   = note: type must outlive the static lifetime

I have a vague idea of what's going on. It looks like there's a chance the closure will outlive the names parameter. I can annotate as 'static but that doesn't feel right and even then I'd like to not move the vector so that do_other_thing works. I need to copy somehow.

回答1:

The error says that names must outlive the static lifetime, this is because the boxed Fn has static lifetime. You have two options:

  1. Add the 'static lifetime to names:

    fn printer(names: Vec<&'static str>) -> Box<Fn() -> String>{
        Box::new(move|| {
            // ...
        })
    }
    
  2. Change the lifetime of the boxed Fn to match the names lifetime:

    fn printer<'a>(names: Vec<&'a str>) -> Box<Fn() -> String + 'a>{
        Box::new(move|| {
            // ...
        })
    }
    

Note that the body of closure needs to be adjusted and that you are giving the ownership of names to printer, so you cannot use names in do_other_thing. Here is a fixed version:

fn main() {
    let names = vec!["foo", "bar", "baz"];
    let print = printer(&names);
    let result = print();
    println!("{}", result);
    do_other_thing(names.as_slice());
}

fn printer<'a>(names: &'a Vec<&str>) -> Box<Fn() -> String + 'a>{
    Box::new(move || {
        // this is more idiomatic
        // map transforms &&str to &str
        names.iter().map(|s| *s).collect()
    })
}


标签: rust