Golang queue: FIFO slice, container/list, and channels

Golang queue implementation for FIFO: slice append and reslice, golang fifo queue memory notes, container/list, buffered channel go queue, generics helpers, peek and empty checks, concurrency hints, and links to channels and generics guides.

Published

Updated

Read time 4 min read

Reviewed byDeepak Prasad

Golang queue: FIFO slice, container/list, and channels

A queue in golang is normally a FIFO structure: enqueue at the rear, dequeue (and optionally peek) at the front. You can implement a golang queue with a slice, with container/list for pointer-based nodes, or with a buffered channel when goroutines coordinate work—each is a valid golang queue implementation with different trade-offs. This page compares those approaches for queues in golang, fixes common typos (peek versus peak), and notes when a golang fifo queue backed only by a slice retains memory. For typed helpers, see generics in Go; for channel semantics, see channels.

Checked with Go 1.24 on 64-bit Linux (Fedora and Ubuntu family).


golang queue implementation patterns

golang fifo queue with a slice

Enqueue with append; dequeue by reslicing with q = q[1:] after you read q[0]. Always check len(q) before accessing the front—otherwise you panic on an empty queue.

go
package main

import "fmt"

func main() {
	queue := make([]int, 0)
	queue = append(queue, 1, 5, 8)
	fmt.Println("enqueue:", queue)

	if len(queue) == 0 {
		return
	}
	fmt.Println("peek:", queue[0])

	queue = queue[1:]
	fmt.Println("dequeue:", queue)

	if len(queue) == 0 {
		fmt.Println("empty")
	} else {
		fmt.Println("not empty, len=", len(queue))
	}
}
Output

You should see enqueue: [1 5 8], peek: 1, then dequeue: [5 8], and a non-empty message.

The underlying array can keep capacity for removed slots; for very long-lived queues with many dequeues, occasionally copy surviving elements into a fresh slice (q = append([]T(nil), q...)) or switch to container/list if profiling shows retained memory matters.

Type-safe queue golang with generics

Generics let one pair of helpers serve many element types ([T any] is enough for a basic FIFO):

go
package main

import "fmt"

func enqueue[T any](q []T, v T) []T { return append(q, v) }

func dequeue[T any](q []T) ([]T, T, bool) {
	if len(q) == 0 {
		var zero T
		return q, zero, false
	}
	return q[1:], q[0], true
}

func peek[T any](q []T) (T, bool) {
	if len(q) == 0 {
		var zero T
		return zero, false
	}
	return q[0], true
}

func main() {
	q := []string{}
	q = enqueue(q, "first")
	q = enqueue(q, "second")
	q = enqueue(q, "third")
	fmt.Println("queue:", q)
	if v, ok := peek(q); ok {
		fmt.Println("peek:", v)
	}
	if nq, front, ok := dequeue(q); ok {
		fmt.Println("popped:", front, "rest:", nq)
	}
}
Output

You should see the three strings enqueued, peek: first, then popped: first with the remaining slice.

queue golang with container/list

list.List stores values in doubly-linked elements, so removing the front does not retain the whole underlying array like a slice can.

go
package main

import (
	"container/list"
	"fmt"
)

func main() {
	q := list.New()
	q.PushBack(36)
	q.PushBack(49)
	q.PushBack(42)

	if f := q.Front(); f != nil {
		q.Remove(f)
	}
	if f := q.Front(); f != nil {
		fmt.Println("peek:", f.Value)
	}
	fmt.Println("isEmpty:", q.Len() == 0)
}
Output

After one dequeue from [36,49,42], the front value should print as 49.

go queue with a buffered channel

A buffered chan is a bounded FIFO between goroutines: sends enqueue, receives dequeue. len on the channel reports queued elements (not capacity). There is still no built-in peek without receiving.

go
package main

import "fmt"

func main() {
	q := make(chan int, 200)
	q <- 5
	q <- 4
	q <- 6

	fmt.Println("dequeue:", <-q)
	fmt.Println("dequeue:", <-q)

	if len(q) == 0 {
		fmt.Println("queue empty")
	} else {
		fmt.Println("remaining:", len(q))
	}
}
Output

You should see 5 and 4 printed, then remaining: 1 because one value remains.

For concurrent producers and consumers, prefer sending and receiving from goroutines instead of assuming len is a synchronization primitive—see channels.

Mutex-wrapped slice for a concurrent queue in go

When multiple goroutines share one slice queue, guard append and reslicing with a sync.Mutex or use a channel instead. A minimal pattern is: lock, append or slice, unlock; never expose the inner slice outside the mutex.


Summary

Queue golang code usually means a FIFO: golang fifo queue with slices is the quickest path; golang queues that live a long time and dequeue heavily may need memory awareness or container/list. A go queue built from a buffered channel fits producer–consumer pipelines but cannot peek the head without extra structure. Golang queue implementation choices come down to threading model, need for peek, and whether you want generics for reusable helpers. Search noise like go-queue often points at third-party broker libraries—in-process work rarely needs a special module name if the patterns above fit.


References


Frequently Asked Questions

1. What is the simplest queue golang pattern?

Use a slice: enqueue with append and dequeue by reslicing past index 0. Check len before peeking or dequeuing to avoid panics.

2. Why does my golang fifo queue slice keep memory after many dequeues?

The underlying array still holds slots for earlier elements until the slice is copied or replaced. If you dequeue millions of items in one long-lived slice, periodically copy remaining elements to a new slice or use container/list or a ring buffer.

3. Can I peek a buffered channel queue in go?

Not without receiving: reading removes the value. If you need peek, keep a slice or list head, duplicate the next value, or use a design where a goroutine owns the channel and exposes peek via another structure.

4. When should I use a channel instead of a slice for a go queue?

Use a buffered channel when producers and consumers are goroutines and you want blocking and close semantics for free. Use a slice when you need random access, peek, or single-threaded in-memory FIFO logic.

5. What do people mean by go-queue as a search term?

It is often a generic phrase for queue code in Go, but some repositories publish a module literally named go-queue for broker-backed workers. For in-process FIFOs, standard patterns below are usually enough.
Tuan Nguyen

Data Scientist

Proficient in Golang, Python, Java, MongoDB, Selenium, Spring Boot, Kubernetes, Scrapy, API development, Docker, Data Scraping, PrimeFaces, Linux, Data Structures, and Data Mining. With expertise …