Skip to main content
This guide walks you through installing Go Container and using three of its data structures: Queue, Stack, and Set.

Prerequisites

  • Go 1.18 or later (generics support is required).
  • A Go module initialized with go mod init.

Steps

1

Install the module

Add github.com/fgm/container to your module:
go get github.com/fgm/container
2

Use a Queue

Import the queue sub-package and create a slice-backed queue with a size hint.
package main

import (
    "fmt"

    "github.com/fgm/container"
    "github.com/fgm/container/queue"
)

type Element int

// sizeHint is an indication of the maximum number of elements expected.
// It is not a hard limit — implementations may use it to pre-allocate storage.
const sizeHint = 100

func main() {
    var e Element = 42

    q := queue.NewSliceQueue[Element](sizeHint)
    q.Enqueue(e)

    // Check length if the implementation supports it.
    if lq, ok := q.(container.Countable); ok {
        fmt.Printf("elements in queue: %d\n", lq.Len())
    }

    // Dequeue returns the element and a boolean indicating success.
    for i := 0; i < 2; i++ {
        e, ok := q.Dequeue()
        fmt.Printf("Element: %v, ok: %t\n", e, ok)
    }
}
Output:
elements in queue: 1
Element: 42, ok: true
Element: 0, ok: false
The second Dequeue returns the zero value and ok: false because the queue is empty. Always check ok before using the returned element.
3

Use a Stack

Import the stack sub-package. The API mirrors the queue — Push and Pop instead of Enqueue and Dequeue.
import "github.com/fgm/container/stack"

s := stack.NewSliceStack[Element](sizeHint)
s.Push(e)

if ls, ok := s.(container.Countable); ok {
    fmt.Printf("elements in stack: %d\n", ls.Len())
}

for i := 0; i < 2; i++ {
    e, ok := s.Pop()
    fmt.Printf("Element: %v, ok: %t\n", e, ok)
}
Output:
elements in stack: 1
Element: 42, ok: true
Element: 0, ok: false
4

Use a Set

Import the set sub-package. Set eliminates duplicates and supports standard set operations.
import (
    "fmt"

    "github.com/fgm/container"
    "github.com/fgm/container/set"
)

func main() {
    var e Element = 42

    s := set.NewBasicMap[Element](sizeHint)

    // Add the squares of 0..41 to the set.
    for i := range e {
        s.Add(i * i)
    }

    if cs, ok := s.(container.Countable); ok {
        fmt.Printf("elements in set: %d\n", cs.Len())
    }

    // Remove elements — Remove is safe to call even for absent items.
    for i := Element(0); i < 10; i++ {
        del := i * i * i
        ok := s.Remove(del)
        fmt.Printf("Element: %3v ok: %t\n", del, ok)
    }
}
5

Use a WaitableQueue for concurrent code

WaitableQueue is the only concurrency-safe type in the module. It uses a WaitChan to signal consumers when items are available, avoiding the capacity constraints of Go channels.
package main

import (
    "fmt"
    "time"

    "github.com/fgm/container"
    "github.com/fgm/container/queue"
)

type Element int

func consumer(wq container.WaitableQueue[Element]) {
    lq := wq.(container.Countable)

    for {
        _, ok := <-wq.WaitChan()
        if !ok {
            fmt.Printf("Consumer exiting, %d remaining\n", lq.Len())
            return
        }
        for {
            item, ok, _ := wq.Dequeue()
            if !ok {
                break
            }
            fmt.Printf("Received: %v, %d in queue\n", item, lq.Len())
        }
    }
}

func main() {
    const sizeHint, low, high = 200, 50, 150

    wq, err := queue.NewWaitableQueue[Element](sizeHint, low, high)
    if err != nil {
        panic(err)
    }

    go consumer(wq)

    for i := range 10 {
        wq.Enqueue(Element(i))
        time.Sleep(2 * time.Millisecond)
    }

    wq.Close() // Unblocks consumers waiting on WaitChan.
    time.Sleep(50 * time.Millisecond)
}
Call Close() on the queue when the producer is done. This unblocks any consumers that are waiting on WaitChan, allowing them to drain remaining items and exit cleanly.

The size hint pattern

Every constructor accepts a sizeHint argument. This is a hint — not a hard cap — that slice-backed implementations use to pre-allocate storage and avoid repeated resizing:
const sizeHint = 100

q := queue.NewSliceQueue[MyType](sizeHint)
s := stack.NewSliceStack[MyType](sizeHint)
om := orderedmap.NewSlice[string, int](sizeHint, true)
Pass 0 if you have no estimate. The implementation will still work correctly, just without the pre-allocation benefit.

Import paths

Data structureImport pathConstructor example
OrderedMapgithub.com/fgm/container/orderedmaporderedmap.NewSlice[K, V](sizeHint, stable)
Queuegithub.com/fgm/container/queuequeue.NewSliceQueue[E](sizeHint)
WaitableQueuegithub.com/fgm/container/queuequeue.NewWaitableQueue[E](sizeHint, low, high)
Setgithub.com/fgm/container/setset.NewBasicMap[E](sizeHint)
Stackgithub.com/fgm/container/stackstack.NewSliceStack[E](sizeHint)
All implementations except WaitableQueue are not safe for concurrent use. Protect them with a sync.Mutex or equivalent when sharing across goroutines.

Next steps

OrderedMap

Explore stable and recency ordering modes.

WaitableQueue

Learn about watermarks and flow control for concurrent queues.

Set operations

Use Union, Intersection, and Difference on your sets.

Choosing an implementation

Pick the right backing store for your performance profile.