+TypeScript example
Playlist owns the items; each iterator owns its current position. Keeping position out of the collection lets two traversals progress independently.
// Hide the internal array and expose only the traversal protocol.
class Playlist implements Iterable<string> {
private readonly tracks: readonly string[];
constructor(tracks: readonly string[]) { this.tracks = [...tracks]; }
// Each call returns a generator with its own traversal position.
*[Symbol.iterator](): Generator<string, void, unknown> {
for (const track of this.tracks) yield track;
}
}
function step(iterator: Iterator<string>): string {
const result = iterator.next();
return result.done ? "(end)" : result.value;
}
const playlist = new Playlist(["intro", "outro"]);
const first = playlist[Symbol.iterator]();
const second = playlist[Symbol.iterator]();
console.log("first: " + step(first));
console.log("first: " + step(first));
console.log("second: " + step(second));
console.log("second: " + step(second));
first: intro
first: outro
second: intro
second: outro
Symbol.iterator provides the entry point corresponding to C# GetEnumerator(). Each generator call creates a separate iterator; after traversal, check the done flag returned by next().