cybozu-go/options
provides Option[T]
, which represents an optional value of type T
.
opt := options.New(42) // or option.None[int]()
fmt.Println(opt) // prints "42"
fmt.Printf("%#v\n", opt) // prints "options.New(42)"
if opt.IsPresent() {
// opt.Unwrap panics when opt is None.
// When there are feasible default values, you can use UnwrapOr or UnwrapOrZero, which do not panic.
v := opt.Unwrap()
DoSomething(v)
}
// You can also use Unpack to check presence and get the value at once.
if v, ok := opt.Unpack(); ok {
DoSomething(v)
}
Option[T]
can be serialized into or deserialized from JSON byencoding/json
.- An
Option[T]
is serialized as if it is*T
.
- An
Option[T]
can be inserted into or selected from databases bydatabase/sql
.Option[string]
is handled as if it issql.NullString
,Option[time.Time]
is handled as if it issql.NullTime
, and so on.
Option[T]
can be compared by google/go-cmp.Option[T].Equal
is implemented sololy for this purpose.
Although Option[T]
can be compared by go-cmp, it has some caveats when T
is complex.
cmp.Diff
may be hard to read because diff ofOption[T]
is shown as diff ofOption[T].String()
.cmp.Diff
does not compare unexported fields of structs by default. On the other hand,Option[T].Equal
is based onreflect.DeepEqual
, which compares unexported fields.
You can use cmp.Transformer
in such case.
type NestedData struct {
Value string
}
type TestData struct {
Value string
Nested *NestedData
}
func TestGoCmp(t *testing.T) {
// Use *T instead of Option[T] in the cmp.Diff.
cmpopt := cmp.Transformer("options.Option", options.Pointer[*TestData])
d1 := options.New(&TestData{
Value: "test",
Nested: &NestedData{
Value: "test",
},
})
d2 := options.New(&TestData{
Value: "test",
Nested: &NestedData{
Value: "test2",
},
})
if diff := cmp.Diff(d1, d2, cmpopt); diff != "" {
t.Errorf("diff:\n%s", diff)
}
}