Create closure returning iterator on string

青春壹個敷衍的年華 提交于 2021-01-29 10:57:35

问题


I want to write a closure that takes an object and returns an iterator from it. The idea is to store the closure in a structure and apply as needed:

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

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

This causes the error:

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

I tried to change String to Vec and remove boxing, but the result is the same.

How can I make it compile?


回答1:


Closures with borrows in parameter or return types have some known bugs as shown in this issue report and the others it links to: https://github.com/rust-lang/rust/issues/58052

There are a few ways to work around the issue.

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



回答2:


I'm not entirely sure what you try to achieve there, but basically just looking at your provided example, you don't need a closure, but a function:

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

Or you could have a closure, that is enclosing the outside world, in this case, your string:

fn main() {
    let test = "test".to_string();
    let iter_wrap = || test.chars();

    for c in iter_wrap() {
        println!("{}", c);
    }
}



回答3:


You need to define and use a lifetime that is shorter than the lifetime of the function itself. I tried this and it worked:

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()
}

Just using lifetime 'a is not sufficient, you need a lifetime that is shorter than that, so 'b: 'a.

Now what I cannot explain is that, with both 'a and 'b defined, using &'a String in the closure definition works as well ...



来源:https://stackoverflow.com/questions/56724730/create-closure-returning-iterator-on-string

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!