Spot the bug - #23

This one is intentionally tricky.

function range(start, end) {
  return {
    [Symbol.iterator]() {
      let i = start;
      return {
        next() {
          if (i < end) return { value: i++, done: false };
          return { done: true };
        }
      };
    }
  };
}

const iter = range(1, 4)[Symbol.iterator]();
const arr = [...iter];
console.log(arr.join(','));

Reply with what is broken and how you would fix it.

Hari

What’s broken is the last bit: you’re spreading an iterator (iter) as if it’s an iterable. The object returned by range(...)[Symbol.iterator]() only has next(), so [...iter] blows up because there’s no iter[Symbol.iterator]().

Fix is either don’t extract the iterator:

const arr = [...range(1, 4)];
console.log(arr.join(','));

…or make the iterator iterable by giving it a [Symbol.iterator]() that returns itself:

return {
  next() { /* ... */ },
  [Symbol.iterator]() { return this; }
};

iter is the iterator, not the iterable, so [...iter] fails unless it has its own [Symbol.iterator]().

Spreading range(1, 4) directly avoids the whole mess.

Yep — iter there is just the iterator object returned by range(...)[Symbol.iterator](), and your iterator doesn’t implement [Symbol.iterator] itself, so it isn’t “iterable” and spread blows up.

Fix is either spread the iterable:

const arr = [...range(1, 4)];

or make the iterator self-iterable by adding:

[Symbol.iterator]() { return this; }

to the object you return from [Symbol.iterator]().

When you said iter comes straight from range(. . . )[Symbol. iterator]() — is there a reason you’re spreading that iterator object instead of just spreading range(1, 4) directly? might be wrong here.

range(1, 4) is enough here since it’s already iterable, so [...range(1, 4)] works fine.

Calling [Symbol.iterator]() gives you the iterator object, which is more for when you want to manually .next() through it. Spreading the iterator works too, it just feels like an extra step in this case.

Yeah spreading the range itself reads cleaner, and grabbing the iterator is like pulling the tape out of a tape measure just to hold the little hook. The only time I reach for [Symbol. iterator]() is when I’m actually doing manual . next() stuff or I need to pass the iterator around.