A generator can be iterated once, and the second foreach over the same instance throws rather than returning nothing.
$rows = readCsv($path);
foreach ($rows as $r) { /* ... */ }
foreach ($rows as $r) { /* ... */ }
// Exception: Cannot traverse an already closed generator
// and rewinding after the first yield:
$g = gen();
$g->current();
$g->next();
$g->rewind(); // Exception: Cannot rewind a generator
// that was already run
Returning a generator from a method is therefore a contract that the caller will use it once, and that is not visible in the return type — iterable and Generator both look reusable. Returning a closure that produces a fresh generator is the fix when reuse is genuinely needed, and it makes the cost explicit. The exception is at least loud; the version of this bug that hurts is a generator passed to two collaborators, one of which silently sees an empty sequence.