1
votes

Returning an iterator from a function in Rust is an exercise of Sisyphean dimensions, but I am told it's possible to return one as a trait without quite so much pain. Unfortunately, it isn't working: apparently, I need an explicit lifetime bound? Which is apparently not the same thing as adding a lifetime parameter. Which means I have no idea how to do that.

Here's my (tiny, test) code:

fn main() {
    let args = get_args();

    for arg in args {
        println!("{}", arg);
    }
}

fn get_args() -> Iterator {
    std::env::args().filter_map(|arg| arg.into_string().ok())
}

What is the appropriate way to make this actually work?

Edit: rust version rustc 1.0.0-nightly (00df3251f 2015-02-08 23:24:33 +0000)

2
To alleviate this issue partly, you are encouraged to provide the version of the compiler you are using, and if using nightly the date and commit ID. This way, it makes it clear to future onlookers that the question and answers might not be applicable to them any longer. - Matthieu M.

2 Answers

2
votes

You can't return a bare Iterator from a function, because it is a trait, thus not a sized type.

In your situation, you'll need to put the iterator object inside a box, in order to make it into a sized object that can be returned from the function.

To do so, you can change your code like this:

fn get_args() -> Box<Iterator<Item=String> + 'static> {
    Box::new(std::env::args().filter_map(|arg| arg.into_string().ok()))
}

Here I've added a lifetime specifier 'static for the trait object, meaning that it is completely self-owned (a function taking no arguments will almost always return something valid for the 'static lifetime in this sense).

You also need the <Item=String> part to explicit the type of data yielded by your iterator. In this case: Strings.

0
votes

In this specific case you can manage to return a concrete type from your get_args, like so:

fn get_args() -> FilterMap<Args, fn(OsString) -> Option<String>> {
    fn arg_into_string(arg: OsString) -> Option<String> { arg.into_string().ok() }
    args().filter_map(arg_into_string as fn(OsString) -> Option<String>)
}

basically this applies to all the cases where the closure you use in the iterator adapter (in your case filter_map) is not really a closure, in that it does not capture any environment, and it can be modeled by a plain old function.

In general, if you do need to return a type that does contain a closure, you will indeed need to box it and return a trait object. In your case:

fn get_args() -> Box<Iterator<Item=String> + 'static> {
    Box::new(std::env::args().filter_map(|arg| arg.into_string().ok()))
}