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 = nil

View on GitHub (pinned to 69932aed18)

Solutions

  1. Guard with `deque.is_empty?` (or `deque.size > 0`) before pop_first/pop_last.
  2. Drive drain loops with `until deque.is_empty?`.
  3. 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

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.