flat_map() is the wrong tool for this task. It is designed to flatten an iterator of iterators, i.e. whenever it encounters None on an inner iterator, its job is to switch to the next iterator. If you keep feeding it empty iterators, it will keep looping until it finds a non-empty one to deliver values from. flat_map() will only terminate iteration when the outer iterator does, which in your case never happens.
What you need is two things: first, mark the end of iteration in some way other than an empty iterator - e.g. by using an Option where Some means "here is another iterator for you" and None means "I'm no longer interested in iteration, you can terminate". Second, you need to terminate the iteration before the flattening step, using an adapter with the capacity to do so, such as take_while() or scan().
Here is a modification of your code that terminates after i reaches 10:
fn main() {
let iter = (0..)
.map(move |i| if i < 10 { Some(0..i) } else { None })
.scan((), |_, item| item)
.flatten();
for i in iter {
println!("{}", i);
}
}
Playground
Here the flat_map() is broken into three parts: first a map() that produces Some(inner_iterator) while we want to iterate, and None when we no longer do. Then comes scan() that transforms the iterator into one whose next() just returns the values returned by the closure. Since the closure returns item (inner iterator wrapped in an option) unchanged, Some(inner_iterator) will pass through, and None will be propagated as the end-of-iteration signal. Finally, flatten() funnels the items produced by inner iterators into a single iterator, as flat_map() did in your original code.
And if you really want to terminate on the first inner iterator that is empty, that can be arranged as well, by extracting the first value in scan() and testing it:
// using 1.. to avoid immediately terminating on 0..0
let iter = (1..)
.map(move |i| if i < 10 { 0..i } else { 0..0 })
.scan((), |_, mut inner| match inner.next() {
Some(first) => Some(once(first).chain(inner)),
None => None,
})
.flatten();
Playground
Note that fuse() is not what you want in any of this. Its purpose is to allow the iterator to be safely queried after exhaustion. Normally an iterator could panic if you call next() after it has already returned None. fuse() extends the iterator contract to allow calling next() after it has already returned None. This is implemented with a separate flag along the inner iterator, and a check of that flag in next(). If you don't query the iterator after it has returned None—which a for loop doesn't—then you have no need for fuse().