Skip to main content
Version: 2.10.x(Latest)

Basic Introduction

A ring structure with concurrency-safe switch support, implemented as a circular doubly linked list.

Use Cases:

The ring data structure is commonly used in low-level development scenarios such as: concurrency lock control, buffer control, circular buffers, sliding windows, etc. The characteristic of ring is that it must have a fixed size. When continuously appending data to the ring, if the data size exceeds the capacity, new values will overwrite old values.

Usage:

import "github.com/gogf/gf/v2/container/gring"

API Documentation:

https://pkg.go.dev/github.com/gogf/gf/v2/container/gring

tip

gring supports chainable operations.

Generic Support

tip

Version requirement: v2.10.0

Starting from v2.10.0, gring provides the generic type TRing[T], offering type-safe ring queue operations.

Basic Usage

Use NewTRing[T] to create a generic ring:

package main

import (
"fmt"
"github.com/gogf/gf/v2/container/gring"
)

func main() {
// Create an integer ring with capacity 10
r := gring.NewTRing[int](10)

// Add elements to the ring
for i := 0; i < 5; i++ {
r.Put(i)
}

fmt.Println("Len:", r.Len()) // Output: Len: 5
fmt.Println("Cap:", r.Cap()) // Output: Cap: 10

// Move to starting position and get value
r.Move(-5)
fmt.Println("Val:", r.Val()) // Output: Val: 0
}

Type Safety Advantages

The generic version provides compile-time type checking, avoiding type assertions:

// Traditional approach (requires type assertion)
r := gring.New(10)
r.Put(1)
val := r.Val().(int) // Type assertion needed

// Generic approach (type-safe)
r := gring.NewTRing[int](10)
r.Put(1)
val := r.Val() // val is directly int type, no assertion needed

Custom Type Example

package main

import (
"fmt"
"github.com/gogf/gf/v2/container/gring"
)

type Player struct {
Position int // Position
Name string // Name
Alive bool // Is alive
}

func main() {
// Create a player ring
r := gring.NewTRing[*Player](10)

// Add players
for i := 1; i <= 10; i++ {
r.Put(&Player{
Position: i,
Name: fmt.Sprintf("Player%d", i),
Alive: true,
})
}

// Traverse all players in the ring
r.Move(-10) // Move to starting position
r.RLockIteratorNext(func(player *Player) bool {
fmt.Printf("Position: %d, Name: %s, Alive: %v\n",
player.Position, player.Name, player.Alive)
return true
})
}

Concurrency-Safe Generic Ring

package main

import (
"fmt"
"sync"
"github.com/gogf/gf/v2/container/gring"
)

func main() {
// Create a concurrency-safe generic ring
r := gring.NewTRing[int](10, true)

var wg sync.WaitGroup

// Concurrent writes
for i := 0; i < 5; i++ {
wg.Add(1)
go func(n int) {
defer wg.Done()
r.Put(n * 10)
}(i)
}

wg.Wait()

fmt.Println("Len:", r.Len())
fmt.Println("Values:", r.SliceNext())
}

Common Methods for Generic Ring

Generic ring TRing[T] provides the following common methods:

MethodDescription
Val() TGet the element value at current position, no type assertion needed
Set(value T) *TRing[T]Set the value at current position
Put(value T) *TRing[T]Set the value at current position and move to next position
Len() intGet the number of used elements in the ring
Cap() intGet the capacity of the ring
Move(n int) *TRing[T]Move n positions
Next() *TRing[T]Move to next position
Prev() *TRing[T]Move to previous position
Link(s *TRing[T]) *TRing[T]Link two rings
Unlink(n int) *TRing[T]Remove n elements from the ring
SliceNext() []TGet all elements slice from current position forward
SlicePrev() []TGet all elements slice from current position backward
RLockIteratorNext(f func(value T) bool)Iterate forward (read lock)
RLockIteratorPrev(f func(value T) bool)Iterate backward (read lock)

Type-Safe Iteration

package main

import (
"fmt"
"github.com/gogf/gf/v2/container/gring"
)

func main() {
r := gring.NewTRing[string](5)
words := []string{"GoFrame", "is", "awesome", "and", "powerful"}

for _, word := range words {
r.Put(word)
}

// Iterate forward - type-safe, no assertion needed
r.Move(-5)
r.RLockIteratorNext(func(word string) bool {
fmt.Printf("Word: %s\n", word)
return true
})
}

Performance Notes

  • gring is implemented based on the standard library container/ring, with added concurrency-safe control and more utility methods
  • Ring structure performs excellently in fixed-size buffer scenarios, avoiding frequent memory allocations
  • The generic version provides better type safety while maintaining the same performance
  • Recommended for use in scenarios requiring circular buffers, sliding windows, etc.