<<<<<<< HEAD
=======
GoDoc Go Report Card Go Coverage License
English | 简体中文
🔥bytedance/gg is a basic library of generics for Go language developed by ByteDance. It is based on the Go 1.18+ generic features and provides efficient, type-safe and rich generic data structures and tool functions.
❓Why this name?
Take the first letter of Go Generics, short and simple.
❓Why choose gg?
- Stable and reliable: It is a necessary tool library for ByteDance R&D team, and it has 1w+ repository references inside.
- Easy to use: With the design principle of simplicity and self-consistent, subcontracted according to functions, modular, semantic intuitive and unified, and low learning cost.
- High Performance: Provides high-performance concurrent data structures, with performance 10+ times faster than standard library.
- No three-party dependencies: Generic libraries will not introduce any three-party dependencies.
- Version control: Follow the SemVer, guaranteeing backward compatibility.
go get github.com/bytedance/gg
- Generic Functional Programming
- Generic Data Processing
- Generic Standard Wrapper
- gsync:Wrap
sync
- gsync:Wrap
- Generic Data Structures
- tuple:Implementation of tuple provides definition of generic n-ary tuples
- set:Implementation of set based on
map[T]struct{} - list:Implementation of doubly linked list
- skipset:High-performance, scalable, concurrent-safe set based on skip-list, up to 15x faster than the built-in
sync.Mapbelow Go 1.24 - skipmap:High-performance, scalable, concurrent-safe map based on skip-list, up to 10x faster than the built-in
sync.Mapbelow Go 1.24
Option type, simplifying the processing of (T, bool)
Usage:
import ( "github.com/bytedance/gg/goption" )
Example:
goption.Of(1, true).Value() // 1 goption.Nil[int]().IsNil() // true goption.Nil[int]().ValueOr(10) // 10 goption.OK(1).IsOK() // true goption.OK(1).ValueOrZero() // 1 goption.OfPtr((*int)(nil)).Ptr() // nil goption.Map(goption.OK(1), strconv.Itoa).Get() // "1" true
Result type, simplifying the processing of (T, error)
Usage:
import ( "github.com/bytedance/gg/gresult" )
Example:
gresult.Of(strconv.Atoi("1")).Value() // 1 gresult.Err[int](io.EOF).IsErr() // true gresult.Err[int](io.EOF).ValueOr(10) // 10 gresult.OK(1).IsOK() // true gresult.OK(1).ValueOrZero() // 1 gresult.Of(strconv.Atoi("x")).Option().Get() // 0 false gresult.Map(gresult.OK(1), strconv.Itoa).Get() // "1" nil
Conditional operation
Usage:
import ( "github.com/bytedance/gg/gcond" )
Example:
gcond.If(true, 1, 2) // 1 var a *struct{ A int } getA := func() int { return a.A } get1 := func() int { return 1 } gcond.IfLazy(a != nil, getA, get1) // 1 gcond.IfLazyL(a != nil, getA, 1) // 1 gcond.IfLazyR(a == nil, 1, getA) // 1 gcond.Switch[string](3). Case(1, "1"). CaseLazy(2, func() string { return "3" }). When(3, 4).Then("3/4"). When(5, 6).ThenLazy(func() string { return "5/6" }). Default("other") // 3/4
Processing value T
Usage:
import ( "github.com/bytedance/gg/gvalue" )
Example1:Zero Value
a := gvalue.Zero[int]() // 0 gvalue.IsZero(a) // true b := gvalue.Zero[*int]() // nil gvalue.IsNil(b) // true gvalue.Or(0, 1, 2) // 1
Example2:Math Operation
gvalue.Max(1, 2, 3) // 3 gvalue.Min(1, 2, 3) // 1 gvalue.MinMax(1, 2, 3) // 1 3 gvalue.Clamp(5, 1, 10) // 5 gvalue.Add(1, 2) // 3
Example3:Comparison
gvalue.Equal(1, 1) // true gvalue.Between(2, 1, 3) // true
Example4:Type Assertion
gvalue.TypeAssert[int](any(1)) // 1 gvalue.TryAssert[int](any(1)) // 1 true
Processing pointer *T
Usage:
import ( "github.com/bytedance/gg/gptr" )
Example:
a := Of(1) gptr.Indirect(a) // 1 b := OfNotZero(1) gptr.IsNotNil(b) // true gptr.IndirectOr(b, 2) // 1 gptr.Indirect(gptr.Map(b, strconv.Itoa)) // "1" c := OfNotZero(0) // nil gptr.IsNil(c) // true gptr.IndirectOr(c, 2) // 2
Processing slice []T
Usage:
import ( "github.com/bytedance/gg/gslice" )
Example1:High-order Function
gslice.Map([]int{1, 2, 3, 4, 5}, strconv.Itoa) // ["1", "2", "3", "4", "5"] isEven := func(i int) bool { return i%2 == 0 } gslice.Filter([]int{1, 2, 3, 4, 5}, isEven) // [2, 4] gslice.Reduce([]int{1, 2, 3, 4, 5}, gvalue.Add[int].Value()) // 15 gslice.Any([]int{1, 2, 3, 4, 5}, isEven) // true gslice.All([]int{1, 2, 3, 4, 5}, isEven) // false
Example2:CURD Operation
gslice.Contains([]int{1, 2, 3, 4, 5}, 2) // true gslice.ContainsAny([]int{1, 2, 3, 4, 5}, 2, 6) // true gslice.ContainsAll([]int{1, 2, 3, 4, 5}, 2, 6) // false gslice.Index([]int{1, 2, 3, 4, 5}, 3.Value()) // 2 gslice.Find([]int{1, 2, 3, 4, 5}, isEven).Value() // 2 gslice.First([]int{1, 2, 3, 4, 5}).Value() // 1 gslice.Get([]int{1, 2, 3, 4, 5}, 1).Value() // 2 gslice.Get([]int{1, 2, 3, 4, 5}, -1).Value() // Access element with negative index // 5
Example3:Partion Operation
gslice.Range(1, 5) // [1, 2, 3, 4] gslice.RangeWithStep(5, 1, -2) // [5, 3] gslice.Take([]int{1, 2, 3, 4, 5}, 2) // [1, 2] gslice.Take([]int{1, 2, 3, 4, 5}, -2) // [4, 5] gslice.Slice([]int{1, 2, 3, 4, 5}, 1, 3) // [2, 3] gslice.Chunk([]int{1, 2, 3, 4, 5}, 2) // [[1, 2], [3, 4], [5]] gslice.Divide([]int{1, 2, 3, 4, 5}, 2) // [[1, 2, 3], [4, 5]] gslice.Concat([]int{1, 2}, []int{3, 4, 5}) // [1, 2, 3, 4, 5] gslice.Flatten([][]int{{1, 2}, {3, 4, 5}}) // [1, 2, 3, 4, 5] gslice.Partition([]int{1, 2, 3, 4, 5}, isEven) // [2, 4], [1, 3, 5]
Example4:Math Operation
gslice.Max([]int{1, 2, 3, 4, 5}).Value() // 5 gslice.Min([]int{1, 2, 3, 4, 5}).Value() // 1 gslice.MinMax([]int{1, 2, 3, 4, 5}).Value().Values() // 1 5 gslice.Sum([]int{1, 2, 3, 4, 5}) // 15
Example5:Convert to map
ToMap([]int{1, 2, 3, 4, 5}, func(i int) (string, int) { return strconv.Itoa(i), i }) // {"1":1, "2":2, "3":3, "4":4, "5":5} ToMapValues([]int{1, 2, 3, 4, 5}, strconv.Itoa) // {"1":1, "2":2, "3":3, "4":4, "5":5} GroupBy([]int{1, 2, 3, 4, 5}, func(i int) string { if i%2 == 0 { return "even" } else { return "odd" } }) // {"even":[2,4], "odd":[1,3,5]}
Example6:Set Operation
gslice.Union([]int{1, 2, 3}, []int{3, 4, 5}) // [1, 2, 3, 4, 5] gslice.Intersect([]int{1, 2, 3}, []int{3, 4, 5}) // [3] gslice.Diff([]int{1, 2, 3}, []int{3, 4, 5}) // [1, 2] gslice.Uniq([]int{1, 1, 2, 2, 3}) // [1, 2, 3] gslice.Dup([]int{1, 1, 2, 2, 3}) // [1, 2]
Example7:Re-order Operation
s1 := []int{5, 1, 2, 3, 4} s2, s3, s4 := Clone(s1), Clone(s1), Clone(s1) Sort(s1) // [1, 2, 3, 4, 5] SortBy(s2, func(i, j int) bool { return i > j }) // [5, 4, 3, 2, 1] StableSortBy(s3, func(i, j int) bool { return i > j }) // [5, 4, 3, 2, 1] Reverse(s4) // [4, 3, 2, 1, 5]
Processing map map[K]V
Usage:
import ( "github.com/bytedance/gg/gmap" )
Example1:Keys / Values Getter
gmap.Keys(map[int]int{1: 2}) // [1] gmap.Values(map[int]int{1: 2}) // [2] gmap.Items(map[int]int{1: 2}).Unzip() // [1] [2] gmap.OrderedKeys(map[int]int{1: 2, 2: 3, 3: 4}) // [1, 2, 3] gmap.OrderedValues(map[int]int{1: 2, 2: 3, 3: 4}) // [2, 3, 4] gmap.OrderedItems(map[int]int{1: 2, 2: 3, 3: 4}).Unzip() // [1, 2, 3] [2, 3, 4] f := func(k, v int) string { return strconv.Itoa(k) + ":" + strconv.Itoa(v) } gmap.ToSlice(map[int]int{1: 2}, f) // ["1:2"] gmap.ToOrderedSlice(map[int]int{1: 2, 2: 3, 3: 4}, f) // ["1:2", "2:3", "3:4"]
Example2:High-order Function
gmap.Map(map[int]int{1: 2, 2: 3, 3: 4}, func(k int, v int) (string, string) { return strconv.Itoa(k), strconv.Itoa(k + 1) }) // {"1":"2", "2":"3", "3":"4"} gmap.Filter(map[int]int{1: 2, 2: 3, 3: 4}, func(k int, v int) bool { return k+v > 3 }) // {"2":2, "3":3}
Example3:CURD Operation
gmap.Contains(map[int]int{1: 2, 2: 3, 3: 4}, 1) // true gmap.ContainsAny(map[int]int{1: 2, 2: 3, 3: 4}, 1, 4) // true gmap.ContainsAll(map[int]int{1: 2, 2: 3, 3: 4}, 1, 4) // false gmap.Load(map[int]int{1: 2, 2: 3, 3: 4}, 1).Value() // 2 gmap.LoadAny(map[int]int{1: 2, 2: 3, 3: 4}, 1, 4).Value() // 2 gmap.LoadAll(map[int]int{1: 2, 2: 3, 3: 4}, 1, 4) // [] gmap.LoadSome(map[int]int{1: 2, 2: 3, 3: 4}, 1, 4) // [2]
Example4:Partion Operation
Chunk(map[int]int{1: 2, 2: 3, 3: 4, 4: 5, 5: 6}, 2) // possible result: [{1:2, 2:3}, {3:4, 4:5}, {5:6}] Divide(map[int]int{1: 2, 2: 3, 3: 4, 4: 5, 5: 6}, 2) // possible result: [{1:2, 2:3, 3:4}, {4:5, 5:6}]
Example5:Math Operation
gmap.Max(map[int]int{1: 2, 2: 3, 3: 4}).Value() // 4 gmap.Min(map[int]int{1: 2, 2: 3, 3: 4}).Value() // 2 gmap.MinMax(map[int]int{1: 2, 2: 3, 3: 4}).Value().Values() // 2 4 gmap.Sum(map[int]int{1: 2, 2: 3, 3: 4}) // 9
Example6:Set Operation
gmap.Union(map[int]int{1: 2, 2: 3, 3: 4}, map[int]int{3: 14, 4: 15, 5: 16}) // {1:2, 2:3, 3:14, 4:15, 5:16} gmap.Intersect(map[int]int{1: 2, 2: 3, 3: 4}, map[int]int{3: 14, 4: 15, 5: 16}) // {3:14} gmap.Diff(map[int]int{1: 2, 2: 3, 3: 4}, map[int]int{3: 14, 4: 15, 5: 16}) // {1:2, 2:3} gmap.UnionBy(gslice.Of(map[int]int{1: 2, 2: 3, 3: 4}, map[int]int{3: 14, 4: 15, 5: 16}), DiscardNew[int, int]()) // {1:2, 2:3, 3:4, 4:15, 5:16} gmap.IntersectBy(gslice.Of(map[int]int{1: 2, 2: 3, 3: 4}, map[int]int{3: 14, 4: 15, 5: 16}), DiscardNew[int, int]()) // {3:4}
Processing function func
Usage:
import ( "github.com/bytedance/gg/gfunc" )
Example1:Partial Application
add := Partial2(gvalue.Add[int]) // convert the Add function into a partial function add1 := add.Partial(1) // Bind (i.e., "freeze") the first argument to 1 add1(0) // 0 + 1 = 1 // 1 add1(1) // Reuse the partially applied function: 1 + 1 = 2 // 2 add1n2 := add1.PartialR(2) // Bind the remaining (rightmost) argument to 2; all arguments are now fixed add1n2() // 1 + 2 = 3
Data type conversion
Usage:
import ( "github.com/bytedance/gg/gconv" )
Example:
gconv.To[string](1) // "1" gconv.To[int]("1") // 1 gconv.To[int]("x") // 0 gconv.To[bool]("true") // true gconv.To[bool]("x") // false gconv.To[int](gptr.Of(gptr.Of(gptr.Of("1")))) // 1 type myInt int type myString string gconv.To[myInt](myString("1")) // 1 gconv.To[myString](myInt(1)) // "1" gconv.ToE[int]("x") // 0 strconv.ParseInt: parsing "x": invalid syntax
Processing JSON
Usage:
import ( "github.com/bytedance/gg/gson" )
Example:
type testStruct struct { Name string `json:"name"` Age int `json:"age"` } testcase := testStruct{Name: "test", Age: 10} gson.Marshal(testcase) // []byte(`{"name":"test","age":10}`) nil gson.MarshalString(testcase) // `{"name":"test","age":10}` nil gson.ToString(testcase) // `{"name":"test","age":10}` gson.MarshalIndent(testcase, "", " ") // "{\n \"name\": \"test\",\n \"age\": 10\n}" nil gson.ToStringIndent(testcase, "", " ") // "{\n \"name\": \"test\",\n \"age\": 10\n}" gson.Valid(`{"name":"test","age":10}`) // true gson.Unmarshal[testStruct](`{"name":"test","age":10}`) // {test 10} nil // Use high-performance JSON codecs such as Sonic or json-iterator, instead of the standard library's encoding/json. import "github.com/bytedance/sonic" gson.MarshalBy(sonic.ConfigDefault, testcase) // []byte(`{"name":"test","age":10}`) nil gson.MarshalString(sonic.ConfigDefault, testcase) // {"name":"test","age":10}`, nil gson.UnmarshalBy[testStruct](sonic.ConfigDefault, `{"name":"test","age":10}`) // testStruct{Name: "test", Age: 10}, nil // Example using Json-Iterator: import jsoniter "github.com/json-iterator/go" gson.MarshalBy(jsoniter.ConfigDefault, testcase) // []byte(`{"name":"test","age":10}`) nil gson.MarshalString(jsoniter.ConfigDefault, testcase) // {"name":"test","age":10}`, nil gson.UnmarshalBy[testStruct](jsoniter.ConfigDefault, `{"name":"test","age":10}`) // testStruct{Name: "test", Age: 10}, nil
Wrap sync
Usage:
import ( "github.com/bytedance/gg/gstd/gsync" )
Example1:gsync.Map wraps sync.Map
sm := gsync.Map[string, int]{} sm.Store("k", 1) sm.Load("k") // 1 true sm.LoadO("k").Value() // 1 sm.Store("k", 2) sm.Load("k") // 2 true sm.LoadAndDelete("k") // 2 true sm.Load("k") // 0 false sm.LoadOrStore("k", 3) // 3 false sm.Load("k") // 3 true sm.ToMap() // {"k":3}
Example2:gsync.Pool wraps sync.Pool
pool := Pool[*int]{ New: func() *int { i := 1 return &i }, } a := pool.Get() *a // 1 *a = 2 pool.Put(a) *pool.Get() // possible result: 1 or 2
Example3:gsync.OnceXXX wraps sync.Once
onceFunc := gsync.OnceFunc(func() { fmt.Println("OnceFunc") }) onceFunc() // "OnceFunc" onceFunc() // (no output) onceFunc() // (no output) i := 1 onceValue := gsync.OnceValue(func() int { i++; return i }) onceValue() // 2 onceValue() // 2 onceValues := gsync.OnceValues(func() (int, error) { i++; return i, nil }) onceValues() // 3 nil onceValues() // 3 nil
Implementation of tuple provides definition of generic n-ary tuples
Usage
import ( "github.com/bytedance/gg/collection/tuple" )
Example:
addr := Make2("localhost", 8080) fmt.Printf("%s:%d\n", addr.First, addr.Second) // localhost:8080 s := Zip2([]string{"red", "green", "blue"}, []int{14, 15, 16}) for _, v := range s { fmt.Printf("%s:%d\n", v.First, v.Second) } // red:14 // green:15 // blue:16 s.Unzip() // ["red", "green", "blue"] [14, 15, 16]
Implementation of set based on map[T]struct{}
Usage
import ( "github.com/bytedance/gg/collection/set" )
Example:
s := New(10, 10, 12, 15) s.Len() // 3 s.Add(10) // false s.Add(11) // true s.Remove(11) && s.Remove(12) // true s.ContainsAny(10, 15) // true s.ContainsAny(11, 12) // false s.ContainsAny() // false s.ContainsAll(10, 15) // true s.ContainsAll(10, 11) // false s.ContainsAll() // true len(s.ToSlice()) // 2
Implementation of doubly linked list
Usage
import ( "github.com/bytedance/gg/collection/list" )
Example:
l := New[int]() e1 := l.PushFront(1) // 1 e2 := l.PushBack(2) // 1->2 e3 := l.InsertBefore(3, e2) // 1->3->2 e4 := l.InsertAfter(4, e1) // 1->4->3->2 l.MoveToFront(e4) // 4->1->3->2 l.MoveToBack(e1) // 4->3->2->1 l.MoveAfter(e3, e2) // 4->2->3->1 l.MoveBefore(e4, e1) // 2->3->4->1 l.Len() // 4 l.Front().Value // 2 l.Back().Value // 1 for e := l.Front(); e != nil; e = e.Next() { fmt.Println(e.Value) // 2 3 4 1 }
High-performance, scalable, concurrent-safe set based on skip-list, up to 15x faster than the built-in sync.Map below Go 1.24
sync.Map, which has better performance compared to skipset in about 90% of use cases.
Usage
import ( "github.com/bytedance/gg/collection/skipset" )
Example:
s := skipset.New[int]() s.Add(10) // true s.Add(10) // false s.Add(11) // true s.Add(12) // true s.Len() // 3 s.Contains(10) // true s.Remove(10) // true s.Contains(10) // false s.ToSlice() // [11, 12] var wg sync.WaitGroup wg.Add(1000) for i := 0; i < 1000; i++ { i := i go func() { defer wg.Done() s.Add(i) }() } wg.Wait() s.Len() // 1000
High-performance, scalable, concurrent-safe map based on skip-list, up to 10x faster than the built-in sync.Map below Go 1.24
sync.Map, which has better performance compared to skipmap in about 90% of use cases.
Usage
import ( "github.com/bytedance/gg/collection/skipmap" )
Example:
s := New[string, int]() s.Store("a", 0) s.Store("a", 1) s.Store("b", 2) s.Store("c", 3) s.Len() // 3 s.Load("a") // 1 true s.LoadAndDelete("a") // 1 true s.LoadOrStore("a", 11) // 11 false gson.ToString(s.ToMap()) // {"a":11, "b":2, "c": 3} s.Delete("a") s.Delete("b") s.Delete("c") var wg sync.WaitGroup wg.Add(1000) for i := 0; i < 1000; i++ { i := i go func() { defer wg.Done() s.Store(strconv.Itoa(i), i) }() } wg.Wait() s.Len() // 1000
gg is licensed under the Apache-2.0 license. See LICENSE for details.
2025 © Bytedance
upstream/main