If I have a typical range definition like the following

    struct Range
    {
        @safe pure @nogc:

        bool empty() const nothrow
        {
            return _i == _j;
        }

        size_t length() const nothrow
        {
            return _j - _i;
        }

        bool front() const
        {
assert(!empty); // TODO use enforce when it's @nogc
            return _store[_i];
        }

        bool back() const
        {
assert(!empty); // TODO use enforce when it's @nogc
            return _store[_j - 1];
        }

        void popFront()
        {
            assert(!empty);
            ++_i;
        }

        void popBack()
        {
            assert(!empty);
            ++_i;
        }

    private:
        BitSet _store;          // copy of store
        size_t _i = 0;         // iterator into _store
        size_t _j = _store.length;
    }

What's the preferred way of reacting to emptyness in the members front, back, popFront, popBack --- using assert, enforce, throw, or simply relying on range-checking in the _store?

And what about using

    debug assert()

instead of

    assert()

typically when `_store`s `opIndex` already performs range-checking?

I think this is a important issue since asserts are not optimized away in release mode and D is very much about performance.

Reply via email to