krahets/hello-algo · error

двусторонняя очередь пуста

Error message

двусторонняя очередь пуста

What it means

This is an unrecoverable Zig @panic raised by the linked-list deque's pop() when the deque is empty. The library uses @panic (not a returned error) because popping from an empty deque is a logic error in the caller, not an expected runtime condition. It fires inside pop(is_front) after isEmpty() returns true, i.e. before any node access, so it prevents the subsequent self.front.?.val unwrap from dereferencing null.

Source

Thrown at ru/codes/zig/chapter_stack_and_queue/linkedlist_deque.zig:100

                node.prev = self.rear;
                self.rear = node;   // Обновить хвостовой узел
            }
            self.que_size += 1;      // Обновить длину очереди
        } 

        // Добавление в голову очереди
        pub fn pushFirst(self: *Self, num: T) !void {
            try self.push(num, true);
        } 

        // Добавление в хвост очереди
        pub fn pushLast(self: *Self, num: T) !void {
            try self.push(num, false);
        } 
        
        // Операция извлечения из очереди
        pub fn pop(self: *Self, is_front: bool) T {
            if (self.isEmpty()) @panic("двусторонняя очередь пуста");
            var val: T = undefined;
            // Операция извлечения из головы очереди
            if (is_front) {
                val = self.front.?.val;     // Временно сохранить значение головного узла
                // Удалить головной узел
                var fNext = self.front.?.next;
                if (fNext != null) {
                    fNext.?.prev = null;
                    self.front.?.next = null;
                }
                self.front = fNext;         // Обновить головной узел
            // Операция извлечения из хвоста очереди
            } else {
                val = self.rear.?.val;      // Временно сохранить значение хвостового узла
                // Удалить хвостовой узел
                var rPrev = self.rear.?.prev;
                if (rPrev != null) {
                    rPrev.?.next = null;

View on GitHub (pinned to 69932aed18)

Solutions

  1. Guard every popFirst()/popLast() call with a preceding `if (!dq.isEmpty())` check, or read dq.size() first.
  2. Track the logical element count in the loop condition so the loop stops when size() reaches 0 instead of issuing an extra pop.
  3. If you need recoverable semantics, wrap the deque and convert the empty case into a returned error or an optional value rather than letting @panic fire.

Example fix

// before
var v = dq.popFirst();
// after
if (dq.isEmpty()) return null;
var v = dq.popFirst();
Defensive patterns

Strategy: validation

Validate before calling

// call BEFORE popFirst()/popLast()
if (dq.isEmpty()) return;   // or return null / an error
const v = dq.popFirst();

Prevention

When it happens

Trigger: Call popFirst() or popLast() (which delegate to pop(true)/pop(false)) on a LinkedListDeque that has zero elements, or that you already drained. Any second pop on a deque containing exactly one element before pushing again will also trip it, since after the first pop the size reaches 0.

Common situations: Draining a deque in a loop and then popping one more element; writing a producer/consumer where the consumer outruns the producer; calling pop() without first consulting size() or isEmpty(); reusing a deque instance after a clear()/drain operation.

Related errors


AI-assisted analysis of krahets/hello-algo@69932aed18 (2026-08-13). Data as JSON: /api/errors/2fd167aba34e0f2c. Report an issue: GitHub.