optionalv2 is a Go package that provides a generic Option[T any] type, inspired by the Option type in languages like Rust. It allows you to represent an optional value: every Option is either Some(value) or None. This can be particularly useful when you need to distinguish between a missing value and a zero value in your applications, especially when dealing with JSON serialization/deserialization.
- Generic Option Type: Supports any type
T, thanks to Go's generics. - Explicit Null Values: Ability to represent explicit
nullvalues when serializing to JSON. - JSON Marshalling/Unmarshalling: Seamless integration with Go's
encoding/jsonpackage. - Convenient Methods: Provides a set of methods for working with optional values, such as
Unwrap(),IsSome(),IsNone(),TakeOr(), etc.
go get github.com/tapp-ai/go-optional-v2import "github.com/tapp-ai/go-optional-v2"To create an Option[T] with a value:
opt := optionalv2.Some(42)Note: If you pass the zero value of the type T to Some, it will be treated as an explicit null when marshalled to JSON. See the Edge Cases and Special Behaviors section for more details.
To create an Option[T] without a value:
opt := optionalv2.None[int]()if opt.IsSome() {
// Option has a value
} else if opt.IsNone() {
// Option is None (no value)
}Retrieves the value inside the Option. If the Option is None, it returns the zero value of type T. Otherwise, it returns the value.
value := opt.Unwrap()Retrieves the value as a pointer. If the Option is None, it returns nil. Otherwise, it returns a pointer to the value.
valuePtr := opt.UnwrapAsPtr()value, err := opt.Take()
if err != nil {
// Handle the error (e.g., Option is None)
}Returns the value if present, otherwise returns the provided fallback value.
value := opt.TakeOr(100) // Returns 100 if opt is NoneReturns the value if present, otherwise executes the provided function and returns its result.
value := opt.TakeOrElse(func() int {
// Compute fallback value
return 200
})Returns the current Option if it has a value, otherwise returns the provided fallback Option.
opt := opt.Or(optionalv2.Some(300))Returns the Option if it satisfies the predicate, otherwise returns None.
opt = opt.Filter(func(v int) bool {
return v > 10
})Executes a function if the Option is Some.
opt.IfSome(func(v int) {
fmt.Println("Value is:", v)
})Executes a function if the Option is None.
opt.IfNone(func() {
fmt.Println("No value")
})fmt.Println(opt.String()) // Outputs: Some[42] or None[]The Option type implements json.Marshaler and json.Unmarshaler, allowing it to be seamlessly serialized and deserialized using the standard encoding/json package.
Important: The Option type should always be used with an omitempty tag in struct fields to ensure correct behavior when marshalling to JSON. Marshalling an Option without omitempty may result in unexpected behavior.
TLDR: In this package, the JSON null is treated as the GoLang zero value (and vice versa). JSON absent fields are treated as None.
- If the
OptionisSomeand contains a non-zero value, it is marshalled as the value. - If the
OptionisSomeand contains the zero value of typeT, it is marshalled asnull. - If the
OptionisNone, it is omitted when marshalling (assumingomitemptyis set in struct tags).
- If the JSON field is absent,
UnmarshalJSONis not called, and theOptionremainsNone. - If the JSON field is present and
null, theOptionbecomesSomewith the zero value of typeT(representing an explicitnull). - If the JSON field has a value, the
OptionbecomesSomewith that value.
type MyStruct struct {
Name optionalv2.Option[string] `json:"name,omitempty"`
Age optionalv2.Option[int] `json:"age,omitempty"`
Birthday optionalv2.Option[time.Time] `json:"birthday,omitempty"`
}s := MyStruct{
Name: optionalv2.Some("Alice"),
Age: optionalv2.None[int](),
Birthday: optionalv2.Some(time.Time{}), // Zero value, will be marshalled as null
}
data, err := json.Marshal(s)
// data will be: { "name": "Alice", "birthday": null }jsonData := []byte(`{ "name": "Bob", "age": null }`)
var s MyStruct
err := json.Unmarshal(jsonData, &s)
// s.Name is Some("Bob")
// s.Age is Some(0) (explicit null)
// s.Birthday is None (field absent)- Zero Values: When you pass the zero value of type
TtoSome, it is treated as an explicitnullwhen marshalling to JSON. This allows you to distinguish between an absent field (None) and a field explicitly set tonull. - Omitted Fields: If an
Optionfield in a struct isNoneand has theomitemptytag, it will be omitted from the JSON output.
package main
import (
"fmt"
"github.com/tapp-ai/go-optional-v2"
)
func main() {
opt := optionalv2.Some(42)
if opt.IsSome() {
fmt.Println("Value is:", opt.Unwrap())
} else {
fmt.Println("No value")
}
optNone := optionalv2.None[int]()
fmt.Println("Has value?", optNone.IsSome())
}package main
import (
"encoding/json"
"fmt"
"github.com/tapp-ai/go-optional-v2"
)
type User struct {
Name optionalv2.Option[string] `json:"name,omitempty"`
Age optionalv2.Option[int] `json:"age,omitempty"`
}
func main() {
user := User{
Name: optionalv2.Some("Charlie"),
Age: optionalv2.Some(0), // Explicit null when marshalled
}
data, _ := json.Marshal(user)
fmt.Println(string(data)) // Output: { "name": "Charlie", "age": null }
}opt := optionalv2.Some(10)
opt.IfSome(func(v int) {
fmt.Println("Value is:", v)
})
opt.IfNone(func() {
fmt.Println("Option is None")
})This package is maintained by the engineering team @ StyleAI.