Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,8 @@ Example2:CURD Operation
```go
gslice.Contains([]int{1, 2, 3, 4, 5}, 2)
// true
gslice.ContainsBy([]int{1, 2, 3, 4, 5}, func(v int) bool { return v == 2 })
// true
gslice.ContainsAny([]int{1, 2, 3, 4, 5}, 2, 6)
// true
gslice.ContainsAll([]int{1, 2, 3, 4, 5}, 2, 6)
Expand Down
2 changes: 2 additions & 0 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,8 @@ gslice.All([]int{1, 2, 3, 4, 5}, isEven)
```go
gslice.Contains([]int{1, 2, 3, 4, 5}, 2)
// true
gslice.ContainsBy([]int{1, 2, 3, 4, 5}, func(v int) bool { return v == 2 })
// true
gslice.ContainsAny([]int{1, 2, 3, 4, 5}, 2, 6)
// true
gslice.ContainsAll([]int{1, 2, 3, 4, 5}, 2, 6)
Expand Down
9 changes: 4 additions & 5 deletions goption/goption.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,6 @@ import (
"encoding/json"
"fmt"
"reflect"

"github.com/bytedance/gg/gvalue"
)

// O represents a generic optional value: Every O is a value T([OK]),
Expand Down Expand Up @@ -107,11 +105,11 @@ func (o O[T]) ValueOr(v T) T {
//
// 💡 HINT: Refer to function [github.com/bytedance/gg/gvalue.Zero]
// for explanation of zero value.
func (o O[T]) ValueOrZero() T {
func (o O[T]) ValueOrZero() (v T) {
if o.ok {
return o.val
}
return gvalue.Zero[T]()
return v
}

// Ptr returns a pointer that points to the internal value of optional value O[T].
Expand Down Expand Up @@ -158,7 +156,8 @@ func (o O[T]) IfNil(f func()) {

// typ returns the string representation of type of optional value.
func (o O[T]) typ() string {
typ := reflect.TypeOf(gvalue.Zero[T]())
var v T
typ := reflect.TypeOf(v)
if typ == nil {
return "any"
}
Expand Down
23 changes: 12 additions & 11 deletions goption/goption_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@ import (
"strconv"
"testing"

"github.com/bytedance/gg/gptr"
"github.com/bytedance/gg/internal/assert"
)

func TestOf(t *testing.T) {
_ = Of(1, true)
_ = OfPtr((*int)(nil))
_ = OfPtr(gptr.Of(1))
v := 1
_ = OfPtr(&v)
}

func TestOValue(t *testing.T) {
Expand All @@ -36,8 +36,8 @@ func TestOValue(t *testing.T) {
assert.Equal(t, 10, Of(10, true).Value())
assert.Equal(t, 10, Of(10, false).Value()) // 💡 NOTE: not recommend
assert.Equal(t, 0, Of(0, false).Value())

assert.Equal(t, 1, OfPtr(gptr.Of(1)).Value())
v := 1
assert.Equal(t, 1, OfPtr(&v).Value())
assert.Equal(t, 0, OfPtr((*int)(nil)).Value())
}

Expand Down Expand Up @@ -130,15 +130,15 @@ func TestJSON(t *testing.T) {
assert.Equal(t, string(expect), string(actual))
}
{
v := gptr.Of(1)
expect, _ := json.Marshal(v)
actual, _ := json.Marshal(OfPtr(v))
v := 1
expect, _ := json.Marshal(&v)
actual, _ := json.Marshal(OfPtr(&v))
assert.Equal(t, string(expect), string(actual))
}
{
v := gptr.Of("test")
expect, _ := json.Marshal(v)
actual, _ := json.Marshal(OfPtr(v))
v := "test"
expect, _ := json.Marshal(&v)
actual, _ := json.Marshal(OfPtr(&v))
assert.Equal(t, string(expect), string(actual))
}

Expand Down Expand Up @@ -282,7 +282,8 @@ func TestOThen(t *testing.T) {
}

func TestOPtr(t *testing.T) {
assert.Equal(t, gptr.Of(10), OK(10).Ptr())
v := 10
assert.Equal(t, &v, OK(v).Ptr())
assert.Equal(t, nil, Nil[int]().Ptr())

// Test modify.
Expand Down
20 changes: 8 additions & 12 deletions gresult/gresult.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,6 @@ import (
"reflect"

"github.com/bytedance/gg/goption"
"github.com/bytedance/gg/gptr"
"github.com/bytedance/gg/gvalue"
)

// R represents a generic result: Every result is a value,
Expand Down Expand Up @@ -87,7 +85,7 @@ func OK[T any](v T) R[T] {
//
// ⚠️ WARNING: Passing a nil error will cause ❌PANIC❌!
func Err[T any](e error) R[T] {
if gvalue.IsNil(e) {
if reflect.ValueOf(e).IsNil() {
panic(fmt.Errorf("expected a non-nil error, but found nil error with type %T", e))
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

想说这样性能会差不少,转头一看 gvalue.IsNil 也已经是反射了(

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

有个问题,原来的gvalue.IsNil()识别不了nil slice

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

有啥好办法吗

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

场景是啥?我印象 var t []Tvar t = []T{} 在使用上应该完全没区别才对?(或者说不会导致什么问题)

Copy link
Copy Markdown
Collaborator Author

@XQ-Gang XQ-Gang Jul 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

从使用上,我也觉得不应该使用 IsNil 或者直接 == nil 来区分空slice。
但是从语义上,IsNil应该能区分出slice是否是nil,之前完全区分不出来,误导性比较强。
之前的实现:

var t []T
gvalue.IsNil(t) // false

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

空 slice 就不是 nil 吧?

		var s []int
		assert.True(t, IsNil(s))
		var s2 = []int{}
		assert.False(t, IsNil(s2))

可以补一个测试。

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes,现在的表现是你发的这样的。
image

}
return R[T]{err: e}
Expand All @@ -112,11 +110,11 @@ func (r R[T]) ValueOr(v T) T {
//
// 💡 HINT: Refer to function [github.com/bytedance/gg/gvalue.Zero]
// for explanation of zero value.
func (r R[T]) ValueOrZero() T {
func (r R[T]) ValueOrZero() (v T) {
if r.err == nil {
return r.val
}
return gvalue.Zero[T]()
return v
}

// Err returns the internal error of R.
Expand Down Expand Up @@ -154,14 +152,15 @@ func (r R[T]) IfErr(f func(error)) {
}

func checkErr(e error) {
if e != nil && gvalue.IsNil(e) {
if e != nil && reflect.ValueOf(e).IsNil() {
panic(fmt.Errorf("error with type %T is nil", e))
}
}

// typ returns the string representation of type of optional value.
func (r R[T]) typ() string {
typ := reflect.TypeOf(gvalue.Zero[T]())
var v T
typ := reflect.TypeOf(v)
if typ == nil {
return "any"
}
Expand All @@ -187,7 +186,8 @@ type jsonR[T any] struct {
func (r R[T]) MarshalJSON() ([]byte, error) {
jr := jsonR[T]{}
if r.err != nil {
jr.Err = gptr.Of(r.err.Error())
msg := r.err.Error()
jr.Err = &msg
} else {
jr.Val = &r.val
}
Expand Down Expand Up @@ -216,14 +216,10 @@ func (r *R[T]) UnmarshalJSON(data []byte) error {
}

if jr.Err == nil && jr.Val == nil {
r.val = gvalue.Zero[T]()
r.err = nil
} else if jr.Err != nil {
r.val = gvalue.Zero[T]()
r.err = errors.New(*jr.Err)
} else {
r.val = *jr.Val
r.err = nil
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里不能直接删除吧?把一个 var r Result 传给 json.Unarmal(data, &v) 后,是需要清除原来的值的?

这么一看发现原来也有问题 😂,应该在 L205 之前就把 r 清空了。

}

return nil
Expand Down
15 changes: 7 additions & 8 deletions gresult/gresult_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import (
"testing"

"github.com/bytedance/gg/goption"
"github.com/bytedance/gg/gvalue"
"github.com/bytedance/gg/internal/assert"
)

Expand Down Expand Up @@ -170,12 +169,12 @@ func TestJSON(t *testing.T) {
var after1 R[int]
err = json.Unmarshal(bs, &after1)
assert.NotNil(t, err)
assert.Equal(t, gvalue.Zero[R[int]](), after1)
assert.Equal(t, OK(0), after1)

var after2 R[float64]
err = json.Unmarshal(bs, &after2)
assert.NotNil(t, err)
assert.Equal(t, gvalue.Zero[R[float64]](), after2)
assert.Equal(t, OK(0.0), after2)

var after3 R[string]
err = json.Unmarshal(bs, &after3)
Expand Down Expand Up @@ -205,13 +204,13 @@ func TestJSON(t *testing.T) {
var r R[string]
err := json.Unmarshal([]byte(`{}`), &r)
assert.Nil(t, err)
assert.Equal(t, gvalue.Zero[R[string]](), r)
assert.Equal(t, OK(""), r)
}
{ // Unmarshal empty: `null`
var r R[string]
err := json.Unmarshal([]byte(`null`), &r)
assert.Nil(t, err)
assert.Equal(t, gvalue.Zero[R[string]](), r)
assert.Equal(t, OK(""), r)
}
{ // Unmarshal error
var r R[string]
Expand All @@ -224,7 +223,7 @@ func TestJSON(t *testing.T) {
err := json.Unmarshal([]byte(`{"value":"test","error":"test"}`), &r)
assert.NotNil(t, err)
t.Log(err)
assert.Equal(t, gvalue.Zero[R[string]](), r)
assert.Equal(t, OK(""), r)
}

{
Expand All @@ -234,7 +233,7 @@ func TestJSON(t *testing.T) {
var r R[bool]
err := json.Unmarshal([]byte(`false`), &r)
assert.NotNil(t, err)
assert.Equal(t, gvalue.Zero[R[bool]](), r)
assert.Equal(t, OK(false), r)
}
{
// Unmarshal illegal: ``
Expand All @@ -243,7 +242,7 @@ func TestJSON(t *testing.T) {
var r R[string]
err := json.Unmarshal([]byte(``), &r)
assert.NotNil(t, err)
assert.Equal(t, gvalue.Zero[R[string]](), r)
assert.Equal(t, OK(""), r)
}
// Struct field
{
Expand Down
14 changes: 14 additions & 0 deletions gslice/gslice.go
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,20 @@ func Contains[T comparable](s []T, v T) bool {
return false
}

// ContainsBy returns whether any element of the slice s satisfies the predicate f.
//
// 🚀 EXAMPLE:
//
// ContainsBy([]int{1, 2, 3}, func(x int) bool { return x > 2 })) ⏩ true
// ContainsBy([]int{1, 2, 3}, func(x int) bool { return x > 3 })) ⏩ false
// ContainsBy([]int{1, 2, 3}, gvalue.IsZero[int])) ⏩ false
// ContainsBy([]int{1, 2, 3}, gvalue.IsNotZero[int])) ⏩ true
//
// 💡 AKA: Any
func ContainsBy[T any](s []T, f func(T) bool) bool {
return Any(s, f)
}

// ContainsAny returns whether any of given elements occur in slice.
//
// 🚀 EXAMPLE:
Expand Down
18 changes: 10 additions & 8 deletions gslice/gslice_example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,15 @@ func Example() {
fmt.Println(All([]int{1, 2, 3, 4, 5}, isEven)) // false

// CRUD operation
fmt.Println(Contains([]int{1, 2, 3, 4, 5}, 2)) // true
fmt.Println(ContainsAny([]int{1, 2, 3, 4, 5}, 2, 6)) // true
fmt.Println(ContainsAll([]int{1, 2, 3, 4, 5}, 2, 6)) // false
fmt.Println(Index([]int{1, 2, 3, 4, 5}, 3).Value()) // 2
fmt.Println(Find([]int{1, 2, 3, 4, 5}, isEven).Value()) // 2
fmt.Println(First([]int{1, 2, 3, 4, 5}).Value()) // 1
fmt.Println(Get([]int{1, 2, 3, 4, 5}, 1).Value()) // 2
fmt.Println(Get([]int{1, 2, 3, 4, 5}, -1).Value()) // 5
fmt.Println(Contains([]int{1, 2, 3, 4, 5}, 2)) // true
fmt.Println(ContainsBy([]int{1, 2, 3, 4, 5}, func(v int) bool { return v == 2 })) // true
fmt.Println(ContainsAny([]int{1, 2, 3, 4, 5}, 2, 6)) // true
fmt.Println(ContainsAll([]int{1, 2, 3, 4, 5}, 2, 6)) // false
fmt.Println(Index([]int{1, 2, 3, 4, 5}, 3).Value()) // 2
fmt.Println(Find([]int{1, 2, 3, 4, 5}, isEven).Value()) // 2
fmt.Println(First([]int{1, 2, 3, 4, 5}).Value()) // 1
fmt.Println(Get([]int{1, 2, 3, 4, 5}, 1).Value()) // 2
fmt.Println(Get([]int{1, 2, 3, 4, 5}, -1).Value()) // 5

// Partion operation
fmt.Println(Range(1, 5)) // [1, 2, 3, 4]
Expand Down Expand Up @@ -97,6 +98,7 @@ func Example() {
// false
// true
// true
// true
// false
// 2
// 2
Expand Down
7 changes: 7 additions & 0 deletions gslice/gslice_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,13 @@ func TestContains(t *testing.T) {
assert.False(t, Contains([]int{}, 5))
}

func TestContainsBy(t *testing.T) {
assert.True(t, ContainsBy([]int{1, 2, 3}, func(x int) bool { return x > 2 }))
assert.False(t, ContainsBy([]int{1, 2, 3}, func(x int) bool { return x > 3 }))
assert.False(t, ContainsBy([]int{1, 2, 3}, gvalue.IsZero[int]))
assert.True(t, ContainsBy([]int{1, 2, 3}, gvalue.IsNotZero[int]))
}

func TestContainsAny(t *testing.T) {
assert.True(t, ContainsAny([]int{0, 1, 2, 3, 4}, 0))
assert.False(t, Contains([]int{0, 1, 2, 3, 4}, 5))
Expand Down
21 changes: 21 additions & 0 deletions gvalue/gvalue.go
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OrO 这个函数名称能直观的表达出你想要的函数意思吗?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OrO 最后一个 O,是啥单词的缩写?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Option。类似 gsync中的LoadO、gconv中的ToR

Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ package gvalue
import (
"reflect"

"github.com/bytedance/gg/goption"
"github.com/bytedance/gg/internal/constraints"
)

Expand Down Expand Up @@ -67,6 +68,26 @@ func Or[T comparable](vals ...T) (v T) {
return
}

// OrO returns the first non-zero value of inputs.
// If all values are zero, return [goption.Nil].
//
// 🚀 EXAMPLE:
//
// OrO(false, true) ⏩ goption.OK(true)
// OrO(0, 1, 2) ⏩ goption.OK(1)
// OrO("", "1", "2") ⏩ goption.OK("1")
// OrO(0, 0, 0) ⏩ goption.Nil[int]()
// OrO("", "", "") ⏩ goption.Nil[string]()
func OrO[T comparable](vals ...T) goption.O[T] {
var zero T
for _, val := range vals {
if val != zero {
return goption.OK(val)
}
}
return goption.Nil[T]()
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

感觉这个函数名字不太直观,名字也不太清晰(还以为是 Or0)。能给一些更 real world 的用例么?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

主要是想迁移原先的choose.NotZero

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

按原来规范可能叫 OfNonZero 比较好?不过还是不如 choose 直观。

// Max returns the maximum value of inputs.
//
// 🚀 EXAMPLE:
Expand Down
10 changes: 10 additions & 0 deletions gvalue/gvalue_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"testing"
"unsafe"

"github.com/bytedance/gg/goption"
"github.com/bytedance/gg/internal/assert"
)

Expand All @@ -42,6 +43,15 @@ func TestOr(t *testing.T) {
assert.Equal(t, 0, Or[int]())
}

func TestOrO(t *testing.T) {
assert.Equal(t, goption.OK(true), OrO(false, false, true))
assert.Equal(t, goption.OK(1), OrO(0, 1, 2))
assert.Equal(t, goption.OK("1"), OrO("", "1", "2"))
assert.Equal(t, goption.Nil[int](), OrO(0, 0, 0))
assert.Equal(t, goption.Nil[string](), OrO("", "", ""))
assert.Equal(t, goption.Nil[int](), OrO[int]())
}

func TestMin(t *testing.T) {
assert.Equal(t, 1, Min(1, 2))
assert.Equal(t, 2, Min(2))
Expand Down
Loading