Why for in loop not break if None is returned?

    let mut x1 = [10, 11, 12, 13, 14, 15, 16].iter();
    let mut x2 = x1.map(|&v| {
        if v == 13 {
            None
        } else {
            Some(v)
        }
    });

    for v in x2 {
        println!("{:?}", v);
    }

The output is like below:
Some(10)
Some(11)
Some(12)
None
Some(14)
Some(15)
Some(16)

I think it should only output Some(10), 11, 12, then break if None is returned.

Why do you think it should stop at the None?

That's not what map does. If you want it to stop after something, you could consider using take_while.

(Note that if it was going to stop on None, then it shouldn't ever output Some(10), but just 10, because giving out options-that-aren't-supposed-to-be-none is just a nuisance for the caller.)

1 Like

@H2CO3
Thanks for your reply.

std::iter - Rust (rust-lang.org)
It says:

First, we call into_iter() on the value. Then, we match on the iterator that returns, calling next over and over until we see a None . At that point, we break out of the loop, and we’re done iterating.

I think for in loop will break if it encounter first None, Does it?

It does. Point is, when you return None from the closure in map, it doesn't come out as None - it comes as Some(None).

3 Likes

But you're not calling next; you're using a for loop, which deals with that for you.

If you want to call next yourself, then you can stop at the inner None like this:

    let mut x1 = [10, 11, 12, 13, 14, 15, 16].iter();
    let mut x2 = x1.map(|&v| if v == 13 { None } else { Some(v) });

    while let Some(Some(v)) = x2.next() {
        println!("{:?}", v);
    }

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=f949c4bf9ac2c8605a3084bc3b697ba1

@Cerber-Ursi @scottmcm
Thanks all for your reply.

The map signature is like this:

fn map<B, F>(self, f: F) -> Map<Self, F>ⓘ
where
    F: FnMut(Self::Item) -> B,

So, the return value of map closure is B, not Option<B>.
As the code of mine above, map closure treats the return value as Some(None) and Some(Some(v)).

Thanks you all again.

Another solution:

+    let mut ignore_from_here = false;
-    let mut x2 = x1.map(|&v| {
+    let mut x2 = x1.filter_map(|&v| {
-        if v == 13 {
+        if v == 13 || ignore_from_here {
+            ignore_from_here = true;
             None

(Playground)

However, it's better to use take_while: See Playground.

Using map will really just modify each element, and if you make the closure return Some or None, then the new iterator will return Some(Some(_)) or Some(None) until it finally returns None. So you're adding an extra Option layer.

Consider this:

fn main() {
    let x1 = [10, 11].into_iter();
    for t in x1 {
        println!("{t:?}");
    }
    let x1 = [10, 11].into_iter();
    let x2 = x1.map(|v| Some(v));
    for t in x2 {
        println!("{t:?}");
    }
    let x1 = [10, 11].into_iter();
    let x2 = x1.map(|v| Some(v));
    let x3 = x2.map(|v| Some(v));
    for t in x3 {
        println!("{t:?}");
    }
}

(Playground)

Output:

10
11
Some(10)
Some(11)
Some(Some(10))
Some(Some(11))

Some more playing:

struct MyIter<'a> {
    data: &'a [i32],
    index: usize,
}

impl<'a> Iterator for MyIter<'a> {
    type Item = i32;
    fn next(&mut self) -> Option<Self::Item> {
        if self.index >= self.data.len() {
            return None;
        }
        let value: i32 = self.data[self.index];
        if value == 13 {
            // if we increment `self.index` here, the iterator will not be fused, but it still works in this example:
            //self.index += 1;
            None
        } else {
            self.index += 1;
            Some(value)
        }
    }
}

fn main() {
    let x1 = MyIter { data: &[10, 11, 12, 13, 14, 15, 16], index: 0 };
    for v in x1 {
        println!("{:?}", v);
    }
}

(Playground) (Playground)

Here, None really means that the iteration is finished. This works even if you comment-in self.index += 1 in the example above. But then the iterator isn't fused.

Edit: I forgot to check self.index >= self.data.len() in the example above and just corrected it.

1 Like

A good mental exercise is to consider what would happen if you didn't explicitly return an Option, ie. no Some or None, just a completely different value. How should the iterator know when to stop in such a case?

1 Like

None is not special like NULL or nil. It doesn't "flatten" automatically, so map preserves what you return without confusing None-from-map with None-from-iterator.

To stop iteration on None, you can use from_fn instead of mapping an existing iterator.

2 Likes

To add to the other solutions, in my opinion the simpliest way to get what you expected is to use Iterator::map_while

6 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.