krahets/hello-algo · error · IndexError
двусторонняя очередь пуста
Error message
двусторонняя очередь пуста
What it means
Raised by LinkedListDeque#pop(is_front) (ru) — the core extraction primitive — when the deque is empty. Both public wrappers delegate here: `pop_first` calls `pop(true)`, `pop_last` calls `pop(false)`, so this single raise covers ALL empty pop operations from either end. The guard precedes the @front/@rear node surgery that would dereference nil.
Source
Thrown at ru/codes/ruby/chapter_stack_and_queue/linkedlist_deque.rb:70
node.prev = @rear
@rear = node # Обновить хвостовой узел
end
@size += 1 # Обновить длину очереди
end
### Добавление в голову очереди ###
def push_first(num)
push(num, true)
end
### Добавление в хвост очереди ###
def push_last(num)
push(num, false)
end
### Операция извлечения из очереди ###
def pop(is_front)
raise IndexError, 'двусторонняя очередь пуста' if is_empty?
# Операция извлечения из головы очереди
if is_front
val = @front.val # Временно сохранить значение головного узла
# Удалить головной узел
fnext = @front.next
unless fnext.nil?
fnext.prev = nil
@front.next = nil
end
@front = fnext # Обновить головной узел
# Операция извлечения из хвоста очереди
else
val = @rear.val # Временно сохранить значение хвостового узла
# Удалить хвостовой узел
rprev = @rear.prev
unless rprev.nil?
rprev.next = nilView on GitHub (pinned to 69932aed18)
Solutions
- Guard with `deque.is_empty?` (or `deque.size > 0`) before pop_first/pop_last.
- Drive drain loops with `until deque.is_empty?`.
- When popping from both ends, re-check size each iteration since each pop changes it.
Example fix
// before deque.pop_first # raises 'двусторонняя очередь пуста' when empty // after val = deque.is_empty? ? nil : deque.pop_first
Defensive patterns
Strategy: validation
Validate before calling
val = deque.pop_first unless deque.is_empty?
Type guard
def deque_nonempty?(d) = !d.is_empty?
Try / catch
begin val = deque.pop_first # also covers pop_last via pop(is_front) rescue IndexError val = nil end
Prevention
- pop(is_front) is the single source — pop_first and pop_last both surface this raise.
- Guard both ends with is_empty?.
- When popping both ends in one loop, re-check size each iteration.
When it happens
Trigger: Calling `deque.pop_first` or `deque.pop_last` on an empty deque. Any drain loop (from either or both ends) that overshoots triggers it once size hits 0.
Common situations: Deque-based sliding window that pops after draining; a palindrome/checker that pops both ends and overshoots; a driver popping more than was pushed.
Related errors
AI-assisted analysis of krahets/hello-algo@69932aed18 (2026-08-13).
Data as JSON: /api/errors/04b8638376bc7301.
Report an issue: GitHub.