0

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?

3 Answers 3

2

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);
    }
}
Sign up to request clarification or add additional context in comments.

Comments

1

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

15 Comments

"It should accept parameter and return an iterator" -- in which case, why does it have to be a closure?
Good question, probably this is my strong C++ background where we have lambda expressions that may be stored as functions or passed to other functions.
@PeterVaro A closure implicitly coerces to a fn pointer if it does not capture anything. This compiles: fn foo(_: fn(i32) -> i32) {} fn main() { foo(|x| x + 2); } Playground
@Alex if you have further requirements, then I suggest to edit your question and the provided example to mention those needs.
@Alex that's exactly the case! It is very hard to come up with a minimal working solution that has all the constraints your actual implementation has. (And IMO that's especially true when it comes to Rust.)
|
0

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 ...

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.