{"record":{"id":"04b8638376bc7301","repo":"krahets/hello-algo","slug":"error-04b863","errorCode":null,"errorMessage":"двусторонняя очередь пуста","messagePattern":"двусторонняя очередь пуста","errorType":"exception","errorClass":"IndexError","httpStatus":null,"severity":"error","filePath":"ru/codes/ruby/chapter_stack_and_queue/linkedlist_deque.rb","lineNumber":70,"sourceCode":"      node.prev = @rear\n      @rear = node # Обновить хвостовой узел\n    end\n    @size += 1 # Обновить длину очереди\n  end\n\n  ### Добавление в голову очереди ###\n  def push_first(num)\n    push(num, true)\n  end\n\n  ### Добавление в хвост очереди ###\n  def push_last(num)\n    push(num, false)\n  end\n\n  ### Операция извлечения из очереди ###\n  def pop(is_front)\n    raise IndexError, 'двусторонняя очередь пуста' if is_empty?\n\n    # Операция извлечения из головы очереди\n    if is_front\n      val = @front.val # Временно сохранить значение головного узла\n      # Удалить головной узел\n      fnext = @front.next\n      unless fnext.nil?\n        fnext.prev = nil\n        @front.next = nil\n      end\n      @front = fnext # Обновить головной узел\n    # Операция извлечения из хвоста очереди\n    else\n      val = @rear.val # Временно сохранить значение хвостового узла\n      # Удалить хвостовой узел\n      rprev = @rear.prev\n      unless rprev.nil?\n        rprev.next = nil","sourceCodeStart":52,"sourceCodeEnd":88,"githubUrl":"https://github.com/krahets/hello-algo/blob/69932aed1891a7b7f6a0de88cd116d3fe13e7032/ru/codes/ruby/chapter_stack_and_queue/linkedlist_deque.rb#L52-L88","documentation":"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.","triggerScenarios":"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.","commonSituations":"Deque-based sliding window that pops after draining; a palindrome/checker that pops both ends and overshoots; a driver popping more than was pushed.","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."],"exampleFix":"// before\ndeque.pop_first  # raises 'двусторонняя очередь пуста' when empty\n\n// after\nval = deque.is_empty? ? nil : deque.pop_first","handlingStrategy":"validation","validationCode":"val = deque.pop_first unless deque.is_empty?","typeGuard":"def deque_nonempty?(d) = !d.is_empty?","tryCatchPattern":"begin\n  val = deque.pop_first  # also covers pop_last via pop(is_front)\nrescue IndexError\n  val = nil\nend","preventionTips":["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."],"tags":["ruby","deque","linkedlist","empty-state","index-error"],"backgroundTag":null,"analyzedSha":"69932aed1891a7b7f6a0de88cd116d3fe13e7032","analyzedAt":"2026-08-13T23:02:37.581Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}