【问题标题】:How to return a Rust closure that borrows state to use later?如何返回借用状态以供以后使用的 Rust 闭包?
【发布时间】:2019-10-02 19:03:48
【问题描述】:

我有一段相当简单的代码。我有一种感觉,我需要用一生来完成这件事,但我现在很难过。

parse_string 是一个函数,它接受对字符串的引用,并返回一个闭包供以后使用,代码如下:

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 | | }
   | |_^

【问题讨论】:

    标签: rust closures lifetime


    【解决方案1】:

    您有许多复杂的问题:

    1. 你需要一个显式的生命周期来连接 string 参数的生命周期和返回闭包的返回值的生命周期。现在,lifetime elision 导致它被推断为与闭包的参数相同。

    2. 您不能通过函数返回对临时对象的引用。它必须是一个独特的变量。

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

    另见:

    【讨论】:

      【解决方案2】:

      您需要在parse_string 中添加显式生命周期注解,以便编译器可以分辨哪些生命周期相同,哪些可能不同。

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

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

      注意Fn(&amp;str) 没有生命周期注解;这是因为传递到 闭包&amp;str 的生命周期与传递到 parse_string&amp;String 的生命周期无关。

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

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

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

      还要注意&amp;String 是一种不寻常的类型,因为它不提供&amp;str 不提供的表达能力。在非泛型代码中使用&amp;String 几乎总是一个错误。请参阅Why is it discouraged to accept a reference to a String (&String), Vec (&Vec), or Box (&Box) as a function argument? 了解更多信息。

      综合起来,这就是我写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 也需要做一些小调整:&amp;String::from("Hello!") 引用了一个临时的String,该临时String 将在行尾立即删除,从而使引用无效。这很容易通过将String 存储在一个变量中来解决,因此它不会在作用域结束之前被删除:

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

      【讨论】:

      • 我实际上不确定为什么 string 在这里被重新借用而不是被移动。猜猜编译器不够聪明
      猜你喜欢
      • 2022-12-01
      • 2019-01-26
      • 1970-01-01
      • 1970-01-01
      • 2016-08-10
      • 2017-04-09
      • 1970-01-01
      • 2022-11-23
      • 1970-01-01
      相关资源
      最近更新 更多