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

Posted

技术标签:

【中文标题】如何为闭包参数声明生命周期?【英文标题】:How to declare a higher-ranked lifetime for a closure argument? 【发布时间】:2015-10-02 22:03:15 【问题描述】:

我想在 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");

Complete sample code on the playground.

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)) 
   |                ^^^^^^^^^^^^^^^

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

【问题讨论】:

也无法管理。当然写fn 作品fn nt&lt;'a&gt;(t : &amp;'a mut SplitWhitespace, line_number: usize) -&gt; Result&lt;&amp;'a str,ParserError&gt; t.next().ok_or(missing_token(line_number)) 相关:***.com/questions/29714934/… 【参考方案1】:

与最初的pointed out by DK. 一样,您可以使用函数将额外的约束应用于闭包的参数和返回值:

fn constrain<F>(f: F) -> F
where
    F: for<'a> Fn(&'a mut SplitWhitespace) -> Result<&'a str, ParserError>,

    f

这为您提供了where 子句的全部功能;在这种情况下,您可以使用 higher-ranked trait bounds (for &lt;...&gt;) 表示闭包必须返回与参数具有相同生命周期的引用。

let nt = constrain(|t| t.next().ok_or(missing_token(line_number)));

归根结底,这是由limitations in Rust's type inference引起的。具体来说,如果一个闭包立即传递给使用它的函数,编译器可以推断出参数和返回类型是什么。不幸的是,当它在使用之前存储在变量中时,编译器不会执行相同级别的推理。

此解决方法有效,因为它立即将闭包传递给函数,确定类型和生命周期引用。

【讨论】:

【参考方案2】:

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

最简单的一种是让闭包直接引用迭代器。


    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.

如果之后您实际上不需要对 tokens 执行任何其他操作,只需执行以下操作:

let mut nt = || tokens.next().ok_or(missing_token(line_number)); 

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

【讨论】:

这回答了问题,而不是问题 ;),但 @PeterSmit 可能正在遭受 XY-Problem @ker 也许有点。我都想要一个好的和惯用的解决方案,并理解为什么某些结构有效或无效。对我来说,目前看来,如果可以选择是否使用显式生命周期(例如范围),大多数情况下最好使用没有显式生命周期的版本【参考方案3】:

&amp;mut SplitWhitespace 实际上是一个&amp;'b mut SplitWhitespace&lt;'a&gt;。这里的相关生命周期是'a,因为它指定了next 返回的字符串切片的存活时间。由于您在 line 参数上应用了 split_whitespace 函数,因此您需要将 'a 设置为与 line 参数相同的生命周期。

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

fn process_string<'a>(line: &'a str, line_number: usize) -> Result<(), ParserError> 

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

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

请注意,虽然这回答了您的问题,但您的问题的正确解决方案是 @A.B.'s solution。

【讨论】:

以上是关于如何为闭包参数声明生命周期?的主要内容,如果未能解决你的问题,请参考以下文章

如何为不同环境的 SwiftUI App 生命周期应用程序运行 UI 测试?

如何为组件的每个实例触发 vue 生命周期事件?

当其中一个是本地引用时,如何为类型约束中的引用编写生命周期?

maven生命周期和插件

无法为返回引用的闭包推断适当的生命周期

maven入门maven的生命周期2