On 01/12/2025 14:26, Tim Düsterhus wrote:
You are wrong here. Manually interacting with an Iterator is not just safe, it is also necessary for some use cases. The simplest example would be iterating two Iterators in lock-step (i.e. doing a `zip()` operation / array_combine()).


It is certainly *possible* to use the Iterator methods safely without a foreach() construct or built-in aggregate; but it's also easy to miss the implications, and cause very confusing behaviour. For instance, you could call next() in two different pieces of code, and each would miss half the items; or you could call rewind() on an iterator that was already in use elsewhere.

The same is true of the ContextManager interface: you could certainly call enterContext() and exitContext() manually with no problems at all. It might even be necessary, for much the same reasons as iterators: you might want to write code that aggregates two context managers in a specific arrangement.


As I understand it, your concern is that someone will call enterContext() without exitContext(). You could do exactly the same thing if you manually handle an Iterator.

Consider this method:

function getDataIterator(): Generator {
    $this->acquireLock();
    while ( $this->hasMoreResults() ) {
        yield $this->getNextResult();
    }
    $this->releaseLock();
}

If the user calls $it = $foo->getDataIterator(), they can call $it->current() and get a result, but never call $it->next(). They could even use it in a foreach() loop but `break` out before consuming all the results. In such cases, the lock would never be released, unless there's an extra safety check in the destructor.


I think the currently proposed names actually make that *less* likely for a ContextManager: if you enter something, you probably want to exit it later.

We could reinforce that further by also prefixing the method names with "__", but we don't have any precedent for that, and misuse would still be possible.


--
Rowan Tommins
[IMSoP]

Reply via email to