null

package module
v0.0.0-...-080183e Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 28, 2023 License: BSD-3-Clause Imports: 5 Imported by: 0

README

null

Go Documentation test codecov

null is a package for providing types to handle nullable values.

For a comparable type V, the nullable type null.T[V] implements sql.Scanner and json.Unmarshaler. It can be used with types such as:

  • Named integer types like time.Duration
  • Structs with comparable fields
  • Arrays consisting of comparable elements

null.T is oriented towards immutability. Therefore, unlike sql.NullInt64, null.T does not have APIs for modification.

  • null.T does not expose its fields.
  • null.T does not have methods for modification (excluding Scan and Unmarshal).
func main() {
	var d null.T[time.Duration]

	// "null" -> null
	json.Unmarshal([]byte(`null`), &d)
	fmt.Printf("valid: %v, value: %v\n", !d.IsNull(), d.ValueOrZero())
	// Output:
	// valid: false, value: 0s

	// "0" -> "0s" (zero value, not null)
	json.Unmarshal([]byte(`0`), &d)
	fmt.Printf("valid: %v, value: %v\n", !d.IsNull(), d.ValueOrZero())
	// Output:
	// valid: true, value: 0s

	// nil -> null
	d.Scan(nil)
	fmt.Printf("valid: %v, value: %v\n", !d.IsNull(), d.ValueOrZero())
	// Output:
	// valid: false, value: 0s
}

Differences from gopkg.in/guregu/null

Differences from the well-known package gopkg.in/guregu/null, which also defines nullable types include:

  • Generics: null supports not only bool, int64, float64, string, and time.Time, but also any comparable type.
  • Immutablity: null.T does not have APIs for modification.
    • null.T does not expose its fields.
    • null.T does not have a SetValid method.
  • Fewer APIs: This package does not provide several APIs defined in guregu/null.
    • New*
    • MarshalText, UnmarshalText
    • SetValid
Minor differences
  • guregu/null.Int can json-unmarshal quoted numbers like "123", but null.T[int64] cannot. This aligns with the behavior when json.Unmarshal is applied to int64.
  • guregu/null.Float can json-unmarshal quoted numbers like "1.23", but null.T[float64] cannot. This aligns with the behavior when json.Unmarshal is applied to float64.
  • For floats with extremely small or large absolute values, the output of MarshalJSON for guregu/null.Float and null.T[float64] is different. null.T[float64] matches the behavior when float64 is passed to json.MarshalJSON.
  • The method IsZero has been renamed to IsNull.

Documentation

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type T

type T[V comparable] struct {
	// contains filtered or unexported fields
}

T represents a value that may be null. The zero value for T is ready for use. It's not recommended to specify composite types with elements of reference types or interface types for V. This is because references may be unintentionally shared.

If V is strictly comparable, T[V] is also strictly comparable. However, if V implements method `Equal(u V) bool`, you should compare values of type T[V] using T.Equal. For details, refer to T.Equal.

Example
package main

import (
	"fmt"

	"github.com/qawatake/null"
)

func main() {
	var i1, i2 null.T[int]
	fmt.Printf("i1 == i2: %v\n", i1 == i2)

	var a1, a2 null.T[[3]bool]
	fmt.Printf("a1 == a2: %v\n", a1 == a2)
	var s1, s2 null.T[struct{ a int }]
	fmt.Printf("s1 == s2: %v\n", s1 == s2)

	// panic: runtime error
	// x1 := null.From[any](map[string]int{})
	// x2 := null.From[any](map[string]int{})
	// fmt.Printf("x1 == x2: %v\n", x1 == x2)

}
Output:

i1 == i2: true
a1 == a2: true
s1 == s2: true

func From

func From[V comparable](v V) T[V]

From creates a new T that is valid.

func FromPtr

func FromPtr[V comparable](p *V) T[V]

FromPtr creates a new T that is null if p is nil.

func (T[V]) Equal

func (t T[V]) Equal(u T[V]) bool

Equal reports whether t and u are equal. Two values t and u are equal if and only if either of the following conditions is met:

  • t and u are both null.
  • t and u are both not null and the internal values are equal in the sense of ==.
  • t and u are both not null and the internal values are equal by `Equal(V) bool`.

Even if t and u are different in terms of ==, they may be equal. So code should use Equal instead of == for comparison.

Example
package main

import (
	"fmt"
	"time"

	"github.com/qawatake/null"
)

func main() {
	// 2012-12-21T04:00:00Z
	x1 := null.From(time.Date(2012, 12, 21, 4, 0, 0, 0, time.UTC))
	// 2012-12-21T06:00:00+02:00
	x2 := null.From(time.Date(2012, 12, 21, 6, 0, 0, 0, time.FixedZone("", 2*60*60)))
	var x3 null.T[time.Time]

	fmt.Printf("x1 == x2: %v\n", x1 == x2)
	fmt.Printf("x1.Equal(x2): %v\n", x1.Equal(x2))
	fmt.Printf("x1.Equal(x3): %v\n", x3.Equal(x1))
}
Output:

x1 == x2: false
x1.Equal(x2): true
x1.Equal(x3): false

func (T[V]) IsNull

func (t T[V]) IsNull() bool

IsNull reports whether t is null.

func (T[V]) MarshalJSON

func (t T[V]) MarshalJSON() ([]byte, error)

MarshalJSON implements the json.Marshaler interface.

func (T[V]) Ptr

func (t T[V]) Ptr() *V

Ptr returns a pointer to the internal value, but it provides a different reference with each call. If t is null (that is, t.IsNull() returns true), it returns nil.

Example
package main

import (
	"fmt"

	"github.com/qawatake/null"
)

func main() {
	i := null.From[int](123)
	p1 := i.Ptr()
	p2 := i.Ptr()
	fmt.Printf("p1 != p2: %v\n", p1 != p2)
}
Output:

p1 != p2: true

func (*T[V]) Scan

func (t *T[V]) Scan(src interface{}) error

Scan implements the sql.Scanner interface.

Example
package main

import (
	"fmt"
	"time"

	"github.com/qawatake/null"
)

func main() {
	var age null.T[time.Duration]
	age.Scan(int64(24 * 1000 * time.Hour))

	fmt.Printf("valid: %v\n", !age.IsNull())
	fmt.Printf("type: %T\n", age.ValueOrZero())
	fmt.Printf("value: %v\n", age.ValueOrZero())
}
Output:

valid: true
type: time.Duration
value: 24000h0m0s

func (*T[V]) UnmarshalJSON

func (t *T[V]) UnmarshalJSON(data []byte) error

UnmarshalJSON implements the json.Unmarshaler interface.

Example
package main

import (
	"encoding/json"
	"fmt"
	"time"

	"github.com/qawatake/null"
)

func main() {
	type ComparableStruct struct {
		Bool   bool
		Int    int64
		Float  float64
		String string
	}
	type ComparableArray [4]int
	type Object struct {
		Duration null.T[time.Duration]
		Struct   null.T[ComparableStruct]
		Array    null.T[ComparableArray]
	}

	var obj1 Object
	data1 := []byte(`{
		"Duration": 1000,
		"Struct": {
			"Bool": true,
			"Int": 123,
			"Float": 1.23,
			"String": "abc"
		},
		"Array": [1,2,3]
	}`)
	json.Unmarshal(data1, &obj1)
	fmt.Println("[obj1]")
	fmt.Printf("duration:\n  valid: %+[1]v\n  type: %[2]T\n  value: %+[2]v\n", !obj1.Duration.IsNull(), obj1.Duration.ValueOrZero())
	fmt.Printf("struct:\n  valid: %+[1]v\n  type: %[2]T\n  value: %+[2]v\n", !obj1.Struct.IsNull(), obj1.Struct.ValueOrZero())
	fmt.Printf("array:\n  valid: %+[1]v\n  type: %[2]T\n  value: %+[2]v\n", !obj1.Array.IsNull(), obj1.Array.ValueOrZero())

	var obj2 Object
	data2 := []byte(`{}`)
	json.Unmarshal(data2, &obj2)
	fmt.Println("[obj2]")
	fmt.Printf("duration:\n  valid: %+[1]v\n  type: %[2]T\n  value: %+[2]v\n", !obj2.Duration.IsNull(), obj2.Duration.ValueOrZero())
	fmt.Printf("struct:\n  valid: %+[1]v\n  type: %[2]T\n  value: %+[2]v\n", !obj2.Struct.IsNull(), obj2.Struct.ValueOrZero())
	fmt.Printf("array:\n  valid: %+[1]v\n  type: %[2]T\n  value: %+[2]v\n", !obj2.Array.IsNull(), obj2.Array.ValueOrZero())
}
Output:

[obj1]
duration:
  valid: true
  type: time.Duration
  value: 1µs
struct:
  valid: true
  type: null_test.ComparableStruct
  value: {Bool:true Int:123 Float:1.23 String:abc}
array:
  valid: true
  type: null_test.ComparableArray
  value: [1 2 3 0]
[obj2]
duration:
  valid: false
  type: time.Duration
  value: 0s
struct:
  valid: false
  type: null_test.ComparableStruct
  value: {Bool:false Int:0 Float:0 String:}
array:
  valid: false
  type: null_test.ComparableArray
  value: [0 0 0 0]

func (T[V]) Value

func (t T[V]) Value() (driver.Value, error)

Value implements the driver.Valuer interface.

func (T[V]) ValueOrZero

func (t T[V]) ValueOrZero() V

ValueOrZero returns the inner value V. If t is null (that is, t.IsNull() returns true), it returns the zero value of V.

Directories

Path Synopsis
internal
sql
Package sql provides a generic interface around SQL (or SQL-like) databases.
Package sql provides a generic interface around SQL (or SQL-like) databases.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL