简体   繁体   中英

Rust: how to return an iterator from a function and use it?

I'm trying to teach myself Rust by working on a little calendar project.

Here I'm trying to generate a list of dates spanning three whole months around a given date. I'd like to return an iterator that can iterate over these dates. Here's my first attempt:

fn three_months_range(tm: time::Tm) -> std::iter::Iterator<Item=time::Tm> {
    let fpm: time::Tm = first_of_previous_month(&tm);
    (0..)
        .map(|i| fpm + time::Duration::days(i))
        .take_while(|&t| t.tm_mon != (tm.tm_mon + 2) % 12)
}

Unfortunately, this doesn't compile and I get an error.

src/main.rs:49:40: 49:75 error: the trait `core::marker::Sized` is not implemented for the type `core::iter::Iterator<Item=time::Tm> + 'static` [E0277]
src/main.rs:49 fn three_months_range(tm: time::Tm) -> std::iter::Iterator <Item=time::Tm> {
                                                      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/main.rs:49:40: 49:75 help: run `rustc --explain E0277` to see a detailed explanation
src/main.rs:49:40: 49:75 note: `core::iter::Iterator<Item=time::Tm> + 'static` does not have a constant size known at compile-time
src/main.rs:49:40: 49:75 note: the return type of a function must have a statically known size

"the return type of a function must have a statically known size". Ok, after some research, it seems that the solution is to return the iterator through a Box. (still, I'm left wondering how the standard library map , filter , take_while ... methods manage to return Iterators and not boxes).

Well, here's the second attempt, which compiles successfully:

fn three_months_range(tm: time::Tm) -> Box<iter::Iterator<Item=time::Tm>> {
    let fpm: time::Tm = first_of_previous_month(&tm);
    Box::new(
        (0..)
        .map(move |i| fpm + time::Duration::days(i))
        .take_while(move |&t| t.tm_mon != (tm.tm_mon + 2) % 12)
    )
}

Unfortunately, I don't manage to use this iterator. For example, let's say I want to build a vector containing the days of the months of each date (1, 2, 3, ..., 31, 1, 2, ..., 30, 1, 2, ... 31):

let days_vec: Vec<u64> = 
    ( *three_months_range(time::now_utc()) )
    .map( |&t: &time::Tm| t.tm_mday )
    .collect();

src/main.rs:14:10: 14:42 error: the trait `core::marker::Sized` is not implemented for the type `core::iter::Iterator<Item=time::Tm> + 'static` [E0277]
src/main.rs:14         .map( |&t: &time::Tm| t.tm_mday )
                        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/main.rs:14:10: 14:42 help: run `rustc --explain E0277` to see a detailed explanation
src/main.rs:14:10: 14:42 note: `core::iter::Iterator<Item=time::Tm> + 'static` does not have a constant size known at compile-time
src/main.rs:15:10: 15:19 error: no method named `collect` found for type `core::iter::Map<core::iter::Iterator<Item=time::Tm> + 'static, [closure@src/main.rs:14:15: 14:40]>` in the current scope
src/main.rs:15         .collect();
                        ^~~~~~~~~
src/main.rs:15:10: 15:19 note: the method `collect` exists but the following trait bounds were not satisfied: `core::iter::Iterator<Item=time::Tm> : core::marker::Sized`, `[closure@src/main.rs:14:15: 14:40] : core::ops::FnMut<(time::Tm,)>`, `core::iter::Map<core::iter::Iterator<Item=time::Tm> + 'static, [closure@src/main.rs:14:15: 14:40]> : core::iter::Iterator`
src/main.rs:14:10: 14:42 error: type mismatch: the type `[closure@src/main.rs:14:15: 14:40]` implements the trait `for<'r> core::ops::FnMut<(&'r time::Tm,)>`, but the trait `core::ops::FnMut<(time::Tm,)>` is required (expected struct `time::Tm`, found &-ptr) [E0281]
src/main.rs:14         .map( |&t: &time::Tm| t.tm_mday )
                        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/main.rs:14:10: 14:42 help: run `rustc --explain E0281` to see a detailed explanation
src/main.rs:14:10: 14:42 error: type mismatch: the type `[closure@src/main.rs:14:15: 14:40]` implements the trait `for<'r> core::ops::FnOnce<(&'r time::Tm,)>`, but the trait `core::ops::FnOnce<(time::Tm,)>` is required (expected struct `time::Tm`, found &-ptr) [E0281]
src/main.rs:14         .map( |&t: &time::Tm| t.tm_mday )
                        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/main.rs:14:10: 14:42 help: run `rustc --explain E0281` to see a detailed explanation
error: aborting due to 4 previous errors

That's a lot of errors.

So what am I doing wrong here?

Is there a relatively simple way to transform iterators in Rust and/or return them from functions?

The problem is that you are trying to take the Iterator out of the Box (which you can't because it's a trait-object and thus not Sized ). But a Box is transparent, you can use map directly on it:

let days_vec: Vec<u64> = 
    three_months_range(time::now_utc())
    .map( |&t: &time::Tm| t.tm_mday )
    .collect();

Note that the map function expects you to take the argument by value and not by reference. So the call can look like this:

.map(|t| t.tm_mday)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM