如何为闭包参数声明生命周期?

Pet*_*mit 17 closures lifetime rust

我想在Rust中为一个闭包声明一个生命周期,但我找不到添加生命周期声明的方法.

use std::str::SplitWhitespace;

pub struct ParserError {
    pub message: String,
}

fn missing_token(line_no: usize) -> ParserError {
    ParserError {
        message: format!("Missing token on line {}", line_no),
    }
}

fn process_string(line: &str, line_number: usize) -> Result<(), ParserError> {
    let mut tokens = line.split_whitespace();

    match try!(tokens.next().ok_or(missing_token(line_number))) {
        "hi" => println!("hi"),
        _ => println!("Something else"),
    }

    // The following code gives "cannot infer appropriate lifetime.....
    // let nt = |t: &mut SplitWhitespace| t.next().ok_or(missing_token(line_number));
    // match try!(nt(&mut tokens)) {
    //     "there" => println!("there"),
    //     _ => println!("_"),
    // }

    // Where should I declare the lifetime 'a?
    // let nt = |t: &'a mut SplitWhitespace| t.next().ok_or(missing_token(line_number));
    // match try!(nt(&mut tokens)) {
    //     "there" => println!("there"),
    //     _ => println!("_"),
    // }

    return Ok(());
}

fn main() {
    process_string("Hi there", 5).ok().expect("Error!!!");
    process_string("", 5).ok().expect("Error!!! 2");
}
Run Code Online (Sandbox Code Playgroud)

在操场上完成示例代码.

error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements
  --> src/main.rs:22:42
   |
22 |     let nt = |t: &mut SplitWhitespace| t.next().ok_or(missing_token(line_number));
   |                                          ^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #2 defined on the body at 22:14...
  --> src/main.rs:22:14
   |
22 |     let nt = |t: &mut SplitWhitespace| t.next().ok_or(missing_token(line_number));
   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   = note: ...so that the types are compatible:
           expected std::iter::Iterator
              found std::iter::Iterator
note: but, the lifetime must be valid for the call at 23:16...
  --> src/main.rs:23:16
   |
23 |     match try!(nt(&mut tokens)) {
   |                ^^^^^^^^^^^^^^^
note: ...so type `std::result::Result<&str, ParserError>` of expression is valid during the expression
  --> src/main.rs:23:16
   |
23 |     match try!(nt(&mut tokens)) {
   |                ^^^^^^^^^^^^^^^
Run Code Online (Sandbox Code Playgroud)

如何声明'a此闭包的生命周期?

oli*_*obk 11

&mut SplitWhitespace实际上是一个&'b mut SplitWhitespace<'a>.这里的相关生命周期是'a,因为它指定next返回的字符串切片有多长.由于您split_whitespaceline参数上应用了该函数,因此需要设置'aline参数具有相同的生命周期.

因此,作为第一步,您将添加生命周期line:

fn process_string<'a>(line: &'a str, line_number: usize) -> Result<(), ParserError> {
Run Code Online (Sandbox Code Playgroud)

然后将生命周期添加到闭包中的类型:

let nt = |t: &mut SplitWhitespace<'a>| t.next().ok_or(missing_token(line_number));
Run Code Online (Sandbox Code Playgroud)

请注意,虽然这可以解答您的问题,但问题的正确解决方案是@ AB的解决方案.


A.B*_*.B. 8

我不知道如何回答你的问题,但有两种方法可以解决这个问题:

最简单的方法是让闭包直接引用迭代器.

{
    let mut nt = || tokens.next().ok_or(missing_token(line_number));
    // call the closure as many times as you need to
}
    // At this point `tokens` will be usable again.
Run Code Online (Sandbox Code Playgroud)

如果你tokens事后并不需要做任何其他事情,那就做:

let mut nt = || tokens.next().ok_or(missing_token(line_number)); 
Run Code Online (Sandbox Code Playgroud)

另一个解决方案是编写一个函数来模拟闭包正在做什么并调用它.


She*_*ter 5

正如DK最初指出的那样。,您可以使用函数将额外的约束应用于闭包的参数并返回值:

fn constrain<F>(f: F) -> F
where
    F: for<'a> Fn(&'a mut SplitWhitespace) -> Result<&'a str, ParserError>,
{
    f
}
Run Code Online (Sandbox Code Playgroud)

这使您可以充分利用该where条款。在这种情况下,您可以使用排名较高的特征边界for <...>)来表示闭包必须返回与该参数寿命相同的引用。

let nt = constrain(|t| t.next().ok_or(missing_token(line_number)));
Run Code Online (Sandbox Code Playgroud)

最终,这是由于Rust的类型推断的局限性引起。具体来说,如果将闭包立即传递给使用它的函数,则编译器可以推断出参数和返回类型是什么。不幸的是,当使用前将其存储在变量中时,编译器不会执行相同级别的推断。

这种解决方法之所以有效,是因为它立即将闭包传递给函数,从而确定类型和生存期引用。