Optional types for Go — a Go-idiomatic Option<T>
Full SQL + JSON integration. Three-state PATCH support. Zero dependencies beyond stdlib.
Go's sql.Null[T] handles SQL but will never get JSON support (closed as infeasible). Pointers (*string) work but have overhead and awkward ergonomics. opt fills this gap permanently.
type User struct {
Name opt.String `json:"name"`
Email opt.String `json:"email,omitzero"`
Age opt.Int `json:"age"`
}
// {"name":"Alice","age":null} — Email omitted (omitzero), Age is explicit null- Generic foundation —
Option[T]works with any type viasql.Null[T] - 9 concrete types — String, Int, Int32, Int16, Float, Bool, Byte, Time
- Three-state
Field[T]— distinguish absent / null / value for PATCH APIs - Functional API —
Map,FlatMap,Equalfor composable transformations - Zero dependencies — only Go stdlib (
database/sql,encoding/json) FieldFromOption— lossless Option→Field conversion for PATCH workflows- SQL-ready —
Scanner/Valuerviasql.Null[T], works with pgx, database/sql - JSON-ready — proper null marshaling,
omitzerosupport (Go 1.24+) - json/v2 compatible — works with
encoding/json/v2without changes zero/subpackage — alternative semantics where zero value = null- Benchmarked — zero-allocation unmarshal, Bool marshal in <1ns
go get github.com/coregx/optRequires Go 1.24+
package main
import (
"encoding/json"
"fmt"
"github.com/coregx/opt"
)
func main() {
// Create optional values
name := opt.StringFrom("Alice")
age := opt.NewInt(0, false) // null
// Safe access with fallbacks
fmt.Println(name.Or("unknown")) // "Alice"
fmt.Println(age.Or(18)) // 18
// JSON marshaling — just works
type User struct {
Name opt.String `json:"name"`
Age opt.Int `json:"age"`
}
data, _ := json.Marshal(User{Name: name, Age: age})
fmt.Println(string(data)) // {"name":"Alice","age":null}
}opt.From(value) // Always valid: opt.From("hello"), opt.From(42)
opt.New(value, valid) // Explicit: opt.New("", false) → null
opt.FromPtr(ptr) // From pointer: nil → null, &v → valid
opt.OrNull(value) // Zero value → null: opt.OrNull("") → null, opt.OrNull(42) → valid
// Type-specific shortcuts
opt.StringFrom("hello") opt.StringOrNull("") // "" → null
opt.IntFrom(42) opt.IntOrNull(0) // 0 → null
opt.FloatFrom(3.14) opt.FloatOrNull(0.0) // 0 → null
opt.BoolFrom(true) opt.BoolOrNull(false) // false → null
opt.TimeFrom(t) opt.TimeOrNull(t) // zero time → null
opt.ByteFrom(0x42) opt.ByteOrNull(0) // 0 → nullFrom = value is always valid. OrNull = zero value means "not set" → null. Choose based on your semantics.
v.Or(fallback) // Value or fallback
v.OrZero() // Value or zero value of T
v.OrElse(func() T) // Value or lazy-computed fallback
v.Ptr() // *T or nil
v.IsZero() // true when null (for omitzero)opt.Map(v, func(T) U) Option[U] // Transform if valid
opt.FlatMap(v, func(T) Option[U]) Option[U] // Chain optional operations
opt.Equal(a, b) // Nil-safe comparisontype PatchUser struct {
Name opt.Field[string] `json:"name,omitzero"`
Email opt.Field[string] `json:"email,omitzero"`
}
// {} → Name.IsAbsent()=true — don't touch
// {"name": null} → Name.IsNull()=true — set to NULL
// {"name": "Alice"} → Name.IsValue()=true — set to "Alice"import "github.com/coregx/opt/zero"
s := zero.StringFrom("") // Invalid — empty string = null
i := zero.IntFrom(0) // Invalid — zero = null
data, _ := json.Marshal(i) // "0" (not "null")| Behavior | opt |
opt/zero |
|---|---|---|
From("") |
Valid (empty string) | Invalid (empty = null) |
From(0) |
Valid (zero int) | Invalid (zero = null) |
| Marshal null | null |
"" / 0 / false |
// Works with database/sql
var user struct {
Name opt.String
Age opt.Int
}
db.QueryRow("SELECT name, age FROM users WHERE id=$1", id).Scan(&user.Name, &user.Age)
// Also works with pgx, sqlx, and other drivers| Feature | opt | guregu/null | *T (pointer) |
sql.Null[T] |
|---|---|---|---|---|
Generic Option[T] |
Full | Partial (MarshalText commented out) | N/A | No JSON |
| Three-state (PATCH) | Field[T] |
No | No | No |
| Map / FlatMap | Yes | No | No | No |
| OrElse (lazy) | Yes | No | No | No |
| JSON marshal | Yes | Yes | Yes | Broken ({"V":...,"Valid":...}) |
| SQL Scanner/Valuer | Yes | Yes | Yes | Yes |
| omitzero | Yes | Yes | No | No |
| Zero-is-null variant | zero/ |
zero/ |
No | No |
| json/v2 compatible | Yes | Yes | Yes | No |
| Legacy code | None | v1→v6 | N/A | N/A |
BenchmarkBoolMarshalJSON 0.85 ns/op 0 allocs
BenchmarkBoolUnmarshalJSON 2.1 ns/op 0 allocs
BenchmarkIntMarshalJSON 48 ns/op 2 allocs
BenchmarkIntUnmarshalJSON 193 ns/op 1 alloc
BenchmarkStringUnmarshalJSON 137 ns/op 0 allocs
BenchmarkStructMarshalJSON 876 ns/op 9 allocs
BenchmarkStructUnmarshalJSON 1116 ns/op 2 allocs
See CONTRIBUTING.md for guidelines.
MIT License — see LICENSE for details.