【问题标题】:Create closure returning iterator on string在字符串上创建闭包返回迭代器
【发布时间】:2019-11-05 13:28:51
【问题描述】:

我想编写一个闭包,它接受一个对象并从中返回一个迭代器。这个想法是将闭包存储在一个结构中并根据需要应用:

fn main() {
    let iter_wrap = |x: &String| Box::new(x.chars());
    let test = String::from("test");

    for x in iter_wrap(&test) {
        println!("{}", x);
    }
}

这会导致错误:

error[E0495]: cannot infer an appropriate lifetime for lifetime parameter in function call due to conflicting requirements
 --> src/main.rs:2:45
  |
2 |     let iter_wrap = |x: &String| Box::new(x.chars());
  |                                             ^^^^^
  |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the body at 2:21...
 --> src/main.rs:2:21
  |
2 |     let iter_wrap = |x: &String| Box::new(x.chars());
  |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...so that reference does not outlive borrowed content
 --> src/main.rs:2:43
  |
2 |     let iter_wrap = |x: &String| Box::new(x.chars());
  |                                           ^
note: but, the lifetime must be valid for the call at 5:14...
 --> src/main.rs:5:14
  |
5 |     for x in iter_wrap(&test) {
  |              ^^^^^^^^^^^^^^^^
note: ...so that argument is valid for the call
 --> src/main.rs:5:14
  |
5 |     for x in iter_wrap(&test) {
  |              ^^^^^^^^^^^^^^^^

我尝试将String改成Vec并去掉拳击,但结果是一样的。

如何编译?

【问题讨论】:

    标签: rust iterator closures lifetime


    【解决方案1】:

    您需要定义和使用比函数本身的生命周期更短的生命周期。我试过了,它奏效了:

    fn foo<'a, 'b: 'a>() {
        let test = String::from("test");
        let iw = |x: &'b String| {
            x.chars()
        };
    
        for x in iw(&test) {
            println!("{}", x);
        }
    }
    
    fn main() {
        foo()
    }
    

    仅使用生命周期 'a 是不够的,您需要一个比这更短的生命周期,所以 'b: 'a

    现在我无法解释的是,在定义了 'a 和 'b 后,在闭包定义中使用 &amp;'a String 也可以...

    【讨论】:

      【解决方案2】:

      在参数或返回类型中借用的闭包有一些已知的错误,如本问题报告所示,以及它链接到的其他错误:https://github.com/rust-lang/rust/issues/58052

      有几种方法可以解决这个问题。

      Using fully qualified syntax

      fn main() {
          let iter_wrap = |x| Box::new(str::chars(x));
          let test = String::from("test");
      
          for x in iter_wrap(&test) {
              println!("{}", x);
          }
      }
      

      Using a type annotation in the closure body

      fn main() {
          let iter_wrap = |x| {let x: &String = x; Box::new(x.chars()) };
          let test = String::from("test");
      
          for x in iter_wrap(&test) {
              println!("{}", x);
          }
      }
      

      【讨论】:

        【解决方案3】:

        我不完全确定你试图在那里实现什么,但基本上只是看看你提供的例子,你不需要闭包,而是一个函数:

        use std::str::Chars;
        
        fn main() {
            fn iter_wrap(s: &str) -> Chars {
                s.chars()
            }
        
            let test = "test".to_string();
        
            for c in iter_wrap(&test) {
                println!("{}", c);
            }
        }
        

        或者你可以有一个闭包,即封闭外部世界,在这种情况下,你的字符串:

        fn main() {
            let test = "test".to_string();
            let iter_wrap = || test.chars();
        
            for c in iter_wrap() {
                println!("{}", c);
            }
        }
        

        【讨论】:

        • “它应该接受参数并返回一个迭代器”——在这种情况下,为什么它必须是一个闭包?
        • 好问题,这可能是我强大的 C++ 背景,我们有 lambda 表达式,可以存储为函数或传递给其他函数。
        • @PeterVaro 如果没有捕获任何内容,则闭包会隐式强制转换为 fn 指针。这编译:fn foo(_: fn(i32) -&gt; i32) {} fn main() { foo(|x| x + 2); }Playground
        • @Alex 如果您有进一步的要求,那么我建议您编辑您的问题和提供的示例以提及这些需求。
        • @Alex 就是这样!很难想出一个最小的工作解决方案,它具有您的实际实施所具有的所有限制。 (当涉及到 Rust 时,IMO 尤其如此。)
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-04-03
        • 1970-01-01
        • 1970-01-01
        • 2012-01-20
        • 2012-10-23
        • 1970-01-01
        • 2015-01-07
        相关资源
        最近更新 更多