krahets/hello-algo · error

雙向佇列為空

Error message

雙向佇列為空

What it means

Unrecoverable Zig @panic from pop(is_front) on the linked-list deque. Guard `if (self.isEmpty()) @panic("雙向佇列為空")` runs before reading `self.front.?.val`, protecting the optional unwrap. Popping an empty deque is treated as a logic error rather than a recoverable empty result.

Source

Thrown at zh-hant/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. Gate every popFirst()/popLast() with `if (!dq.isEmpty())` or a `dq.size() > 0` check.
  2. Bound drain loops on size() so they stop exactly when the deque empties.
  3. Add a wrapper returning ?T if pop-on-empty is a legitimate control-flow signal in your code.

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 null;
var v = dq.popFirst();

Prevention

When it happens

Trigger: Call popFirst()/popLast() (both delegate to pop()) on an empty deque, or pop a deque that has been drained to size 0. Any pop issued after the last element was removed will trip it.

Common situations: Drain loops that pop one extra time; sliding-window / monotonic-deque algorithms that pop both ends and overshoot when the window is empty; producer/consumer where the consumer leads the producer.

Related errors


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