如何 return 借用状态以备后用的 Rust 闭包?

How to return a Rust closure that borrows state to use later?

我有一段相当简单的代码。我有一种感觉,我需要用一辈子的时间来完成这个,但我现在很困惑。

parse_string是一个接受字符串引用的函数,returns是后面要用到的闭包,代码如下:

fn main() {
    let parse_this = parse_string(&String::from("Hello!"));
    println!("{}", parse_this("goodbye!"));
}

fn parse_string(string: &String) -> impl Fn(&str) -> &String {
    return |targetString| {
        // pretend there is parsing logic
        println!("{}", targetString);
        return string;
    };
}

编译器错误:

error: cannot infer an appropriate lifetime
  --> src/main.rs:7:12
   |
6  |   fn parse_string(string: &String) -> impl Fn(&str) -> &String {
   |                                       ------------------------ this return type evaluates to the `'static` lifetime...
7  |       return |targetString| {
   |  ____________^
8  | |         // pretend there is parsing logic
9  | |         println!("{}", targetString);
10 | |         return string;
11 | |     };
   | |_____^ ...but this borrow...
   |
note: ...can't outlive the anonymous lifetime #1 defined on the function body at 6:1
  --> src/main.rs:6:1
   |
6  | / fn parse_string(string: &String) -> impl Fn(&str) -> &String {
7  | |     return |targetString| {
8  | |         // pretend there is parsing logic
9  | |         println!("{}", targetString);
10 | |         return string;
11 | |     };
12 | | }
   | |_^
help: you can add a constraint to the return type to make it last less than `'static` and match the anonymous lifetime #1 defined on the function body at 6:1
   |
6  | fn parse_string(string: &String) -> impl Fn(&str) -> &String + '_ {
   |                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error[E0312]: lifetime of reference outlives lifetime of borrowed content...
  --> src/main.rs:10:16
   |
10 |         return string;
   |                ^^^^^^
   |
note: ...the reference is valid for the anonymous lifetime #2 defined on the body at 7:12...
  --> src/main.rs:7:12
   |
7  |       return |targetString| {
   |  ____________^
8  | |         // pretend there is parsing logic
9  | |         println!("{}", targetString);
10 | |         return string;
11 | |     };
   | |_____^
note: ...but the borrowed content is only valid for the anonymous lifetime #1 defined on the function body at 6:1
  --> src/main.rs:6:1
   |
6  | / fn parse_string(string: &String) -> impl Fn(&str) -> &String {
7  | |     return |targetString| {
8  | |         // pretend there is parsing logic
9  | |         println!("{}", targetString);
10 | |         return string;
11 | |     };
12 | | }
   | |_^

您有许多复合问题:

  1. 您需要一个明确的生命周期来将 string 参数的生命周期连接到 returned 闭包的 return 值的生命周期。现在,lifetime elision 导致它被推断为与闭包的 参数 相同。

  2. 您不能 return 通过函数引用临时对象。它需要是一个不同的变量。

  3. 您必须将 string 移动到闭包中以防止再次引用它,这不会足够长。

此外...

  1. targetString 应该是 target_string 以遵循 Rust 习语。
  2. return 不应在块的末尾使用以遵循 Rust 习语。
  3. &str 通常优于 &String
fn main() {
    let s = String::from("Hello!");
    let parse_this = parse_string(&s);
    println!("{}", parse_this("goodbye!"));
}

fn parse_string<'a>(string: &'a String) -> impl Fn(&str) -> &'a String {
    return move |target_string| {
        // pretend there is parsing logic
        println!("{}", target_string);
        string
    };
}

另请参阅:

您需要向 parse_string 添加显式生命周期注释,以便编译器可以判断哪些生命周期相同,哪些可能不同。

Fn(&str) -> &String 将是 returns 与传入的 &str 生命周期相同的 &String 函数的类型;即 for<'b> Fn(&'b str) -> &'b String。您需要说明返回的 &String 与传入 parse_string&String 具有相同的生命周期:

fn parse_string<'a>(string: &'a String) -> impl Fn(&str) -> &'a String {

请注意 Fn(&str) 没有生命周期注释;这是因为传递给 闭包 &str 的生命周期与传递给 parse_string.[=40= 的 &String 的生命周期无关]

为了parse_string编译,您需要再做一处改动。如果编译器认为不需要移动闭包,则闭包会尝试借用它们的环境。借用 string 的闭包不能从 string 是局部变量的函数返回。要解决此问题,您 move 将捕获的变量放入闭包中:

    move |target_string| {
        // pretend there is parsing logic
        println!("{}", target_string);
        string
    }

在函数的最后一个表达式中省略 return 在 Rust 中是惯用的。

另请注意,&String 是一种不常见的类型,因为它不提供 &str 不提供的表现力。在非通用代码中使用 &String 几乎总是错误的。有关详细信息,请参阅

综合起来,我会这样写 parse_string:

fn parse_string<'a>(string: &'a str) -> impl Fn(&str) -> &'a str {
    move |target_string| {
        // pretend there is parsing logic
        println!("{}", target_string);
        string
    }
}

你的 main 也需要一个小的调整:&String::from("Hello!") 引用了一个临时的 String ,它将在行尾立即删除,使引用无效。这很容易通过将 String 存储在一个变量中来解决,这样它就不会被删除,直到范围结束:

fn main() {
    let hello = String::from("Hello!");
    let parse_this = parse_string(&hello);
    println!("{}", parse_this("goodbye!"));
}