Bottom line: external iteration is composable, internal iteration isn't. Try implementing zip or early stopping using internal iteration (hint: you can't).
I find it easier to think of as "push-pull" though. In Python and Go, you "pull" from iterators. In Ruby, JavaScript, and Rust, you're "pushed" upon. You can do both styles in any language, but there's one way that's favored.
To break the dilemma, you need 2 call stacks, which means threads / coroutines / generators.
(There was a recent blog post about writing an iterator to do the powerset calculation, which is a bit tricky, and made me think of this post)
Bottom line: external iteration is composable, internal iteration isn't. Try implementing zip or early stopping using internal iteration (hint: you can't).