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
- Guard every popFirst()/popLast() call with a preceding `if (!dq.isEmpty())` check, or read dq.size() first.
- Track the logical element count in the loop condition so the loop stops when size() reaches 0 instead of issuing an extra pop.
- 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
- Always read size() or isEmpty() before any pop on a deque.
- Drive drain loops with `while (!dq.isEmpty())` so the loop cannot issue an extra pop.
- Keep producer/consumer in sync so the consumer never leads by more pops than pushes.
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.