diff --git a/.golangci.yml b/.golangci.yml index abab1ab..fafdbb0 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -20,10 +20,13 @@ linters: exclusions: rules: - - path-except: cmd/generator/*.go + - path: cmd/generator/ linters: - forbidigo # fmt functions are not forbidden here - gochecknoglobals # global variables are not forbidden here + - path: _test.go + linters: + - wrapcheck settings: godot: diff --git a/bool_gen.go b/bool_gen.go index dcc82f4..ac419f7 100644 --- a/bool_gen.go +++ b/bool_gen.go @@ -14,6 +14,8 @@ type Bool struct { exists bool } +var _ commonInterface[bool] = (*Bool)(nil) + // SomeBool creates an optional Bool with the given bool value. // The returned Bool will have IsSome() == true and IsZero() == false. // diff --git a/byte_gen.go b/byte_gen.go index 7a3a3ff..52edc11 100644 --- a/byte_gen.go +++ b/byte_gen.go @@ -14,6 +14,8 @@ type Byte struct { exists bool } +var _ commonInterface[byte] = (*Byte)(nil) + // SomeByte creates an optional Byte with the given byte value. // The returned Byte will have IsSome() == true and IsZero() == false. // diff --git a/bytes_gen.go b/bytes_gen.go index 358dc0a..a994e8c 100644 --- a/bytes_gen.go +++ b/bytes_gen.go @@ -14,6 +14,8 @@ type Bytes struct { exists bool } +var _ commonInterface[[]byte] = (*Bytes)(nil) + // SomeBytes creates an optional Bytes with the given []byte value. // The returned Bytes will have IsSome() == true and IsZero() == false. // diff --git a/cmd/generator/generator.go b/cmd/generator/generator.go index bf3c535..857ab24 100644 --- a/cmd/generator/generator.go +++ b/cmd/generator/generator.go @@ -239,6 +239,8 @@ type {{.Name}} struct { exists bool } +var _ commonInterface[{{.Type}}] = (*{{.Name}})(nil) + // Some{{.Name}} creates an optional {{.Name}} with the given {{.Type}} value. // The returned {{.Name}} will have IsSome() == true and IsZero() == false. // diff --git a/doc.go b/doc.go index 5f963c3..baf5eb7 100644 --- a/doc.go +++ b/doc.go @@ -1,5 +1,5 @@ // Package option provides a type-safe way to represent optional values in Go. -// An Optional[T] can either contain a value of type T (Some) or be empty (None). +// A Generic[T] can either contain a value of type T (Some) or be empty (None). // // This is useful for: // - Clearly representing nullable fields in structs. diff --git a/errors.go b/errors.go index d0e6b85..e2e1955 100644 --- a/errors.go +++ b/errors.go @@ -28,6 +28,10 @@ func newDecodeWithCodeError(operationType string, code byte) error { } } +func getGenericTypeName[T any]() string { + return fmt.Sprintf("Generic[%T]", zero[T]()) +} + func newDecodeError(operationType string, err error) error { if err == nil { return nil @@ -40,6 +44,18 @@ func newDecodeError(operationType string, err error) error { } } +func newDecodeGenericError[T any](err error) error { + if err == nil { + return nil + } + + return DecodeError{ + Type: getGenericTypeName[T](), + Code: NoneByte(), + Parent: err, + } +} + // EncodeError is returned when encoding failed due to stream errors. type EncodeError struct { Type string @@ -58,3 +74,11 @@ func newEncodeError(operationType string, err error) error { return EncodeError{Type: operationType, Parent: err} } + +func newEncodeGenericError[T any](err error) error { + if err == nil { + return nil + } + + return EncodeError{Type: getGenericTypeName[T](), Parent: err} +} diff --git a/errors_test.go b/errors_test.go index b911420..c70ed3d 100644 --- a/errors_test.go +++ b/errors_test.go @@ -1,4 +1,5 @@ package option //nolint:testpackage + // this is unit test, that checks internal logic of error constructing. import ( @@ -13,7 +14,7 @@ var ( errTest = errors.New("some error") ) -func TestDecodeError_Error(t *testing.T) { +func TestEncodeError_Error(t *testing.T) { t.Parallel() t.Run("newEncodeError with error", func(t *testing.T) { @@ -32,9 +33,26 @@ func TestDecodeError_Error(t *testing.T) { require.NoError(t, a) }) + + t.Run("newEncoderGenericError", func(t *testing.T) { + t.Parallel() + + a := newEncodeGenericError[int](errTest) + + require.Error(t, a) + assert.Equal(t, "failed to encode Generic[int]: some error", a.Error()) + }) + + t.Run("newEncodeGenericError without error", func(t *testing.T) { + t.Parallel() + + a := newEncodeGenericError[int](nil) + + require.NoError(t, a) + }) } -func TestEncodeError_Error(t *testing.T) { +func TestDecodeError_Error(t *testing.T) { t.Parallel() t.Run("newDecodeError with error", func(t *testing.T) { @@ -62,4 +80,21 @@ func TestEncodeError_Error(t *testing.T) { require.Error(t, a) assert.Equal(t, "failed to decode Byte, invalid code: 1", a.Error()) }) + + t.Run("newDecodeGenericError", func(t *testing.T) { + t.Parallel() + + a := newDecodeGenericError[int](errTest) + + require.Error(t, a) + assert.Equal(t, "failed to decode Generic[int]: some error", a.Error()) + }) + + t.Run("newDecodeGenericError without error", func(t *testing.T) { + t.Parallel() + + a := newDecodeGenericError[int](nil) + + require.NoError(t, a) + }) } diff --git a/float32_gen.go b/float32_gen.go index 83e248e..262b394 100644 --- a/float32_gen.go +++ b/float32_gen.go @@ -14,6 +14,8 @@ type Float32 struct { exists bool } +var _ commonInterface[float32] = (*Float32)(nil) + // SomeFloat32 creates an optional Float32 with the given float32 value. // The returned Float32 will have IsSome() == true and IsZero() == false. // diff --git a/float64_gen.go b/float64_gen.go index dc5fdf6..50a0a16 100644 --- a/float64_gen.go +++ b/float64_gen.go @@ -14,6 +14,8 @@ type Float64 struct { exists bool } +var _ commonInterface[float64] = (*Float64)(nil) + // SomeFloat64 creates an optional Float64 with the given float64 value. // The returned Float64 will have IsSome() == true and IsZero() == false. // diff --git a/generic.go b/generic.go new file mode 100644 index 0000000..268bd81 --- /dev/null +++ b/generic.go @@ -0,0 +1,242 @@ +package option + +import ( + "github.com/vmihailenco/msgpack/v5" + "github.com/vmihailenco/msgpack/v5/msgpcode" +) + +// Generic represents an optional value: it may contain a value of type T (Some), +// or it may be empty (None). +// +// This type is useful for safely handling potentially absent values without relying on +// nil pointers or sentinel values, and avoids panics when proper checks are used. +// +// Example: +// +// opt := option.Some(42) +// if opt.IsSome() { +// fmt.Println(opt.Unwrap()) // prints 42 +// } +// +// var empty option.Generic[string] +// fmt.Println(empty.IsZero()) // true +type Generic[T any] struct { + value T + exists bool +} + +var _ commonInterface[any] = (*Generic[any])(nil) + +// Some creates a Generic[T] containing the given value. +// +// The returned Generic is in the "some" state, meaning IsSome() will return true. +// +// Example: +// +// opt := option.Some("hello") +// fmt.Println(opt.IsSome()) // true +func Some[T any](value T) Generic[T] { + return Generic[T]{ + value: value, + exists: true, + } +} + +// None creates an Generic[T] that does not contain a value. +// +// The returned Generic is in the "none" state, meaning IsZero() will return true. +// +// Example: +// +// opt := option.None[int]() +// fmt.Println(opt.IsZero()) // true +func None[T any]() Generic[T] { + return Generic[T]{exists: false} //nolint:exhaustruct +} + +// IsSome returns true if the optional contains a value. +// +// Example: +// +// if opt.IsSome() { +// // safely access value +// } +func (o Generic[T]) IsSome() bool { + return o.exists +} + +// IsZero returns true if the optional does not contain a value. +// +// Example: +// +// if opt.IsZero() { +// log.Println("value is missing") +// } +func (o Generic[T]) IsZero() bool { + return !o.exists +} + +// IsNil is an alias for IsZero. +// +// This method is provided for compatibility with the msgpack Encoder interface. +func (o Generic[T]) IsNil() bool { + return o.IsZero() +} + +// Get returns the contained value and a boolean indicating whether the value exists. +// +// This is the safest way to extract the value. The second return value is true if a value +// is present, false otherwise. The first return value is the zero value of T when no value exists. +// +// Example: +// +// value, ok := opt.Get() +// if ok { +// process(value) +// } else { +// fmt.Println("no value available") +// } +func (o Generic[T]) Get() (T, bool) { + return o.value, o.exists +} + +// MustGet returns the contained value if present. +// +// Panics if the optional is in the "none" state (i.e., no value is present). +// +// Only use this method when you are certain the value exists. +// For safer access, use Get() instead. +// +// Example: +// +// value := opt.MustGet() // panics if value not set +func (o Generic[T]) MustGet() T { + if !o.exists { + panic("optional value is not set") + } + + return o.value +} + +// Unwrap returns the stored value regardless of presence. +// If no value is set, returns the zero value for T. +// +// Warning: Does not check presence. Use IsSome() before calling if you need +// to distinguish between absent value and explicit zero value. +func (o Generic[T]) Unwrap() T { + return o.value +} + +// UnwrapOr returns the contained value if present, otherwise returns the provided default value. +// +// This is useful when you want to provide a simple fallback value. +// +// Example: +// +// name := opt.UnwrapOr("default") +func (o Generic[T]) UnwrapOr(defaultValue T) T { + if o.exists { + return o.value + } + + return defaultValue +} + +// UnwrapOrElse returns the contained value if present, otherwise calls the provided function +// to compute a default value. +// +// This is useful when the default value is expensive to compute, or requires dynamic logic. +// +// Example: +// +// value := opt.UnwrapOrElse(fetchFromDatabase) +func (o Generic[T]) UnwrapOrElse(defaultValueFunc func() T) T { + if o.exists { + return o.value + } + + return defaultValueFunc() +} + +// convertToEncoder checks whether the given value implements msgpack.CustomEncoder. +// +// Used internally during encoding to support custom MessagePack encoding logic. +func convertToEncoder(v interface{}) (msgpack.CustomEncoder, bool) { + enc, ok := v.(msgpack.CustomEncoder) + return enc, ok +} + +// EncodeMsgpack implements the msgpack.CustomEncoder interface. +// +// If the optional is empty (None), it encodes as a MessagePack nil. +// If the optional contains a value (Some), it attempts to use a custom encoder if the value +// implements msgpack.CustomEncoder; otherwise, it uses the standard encoder. +func (o Generic[T]) EncodeMsgpack(encoder *msgpack.Encoder) error { + if !o.exists { + err := encoder.EncodeNil() + if err != nil { + return newEncodeGenericError[T](err) + } + + return nil + } + + encoderValue, ok := convertToEncoder(&o.value) + + var err error + if ok { + err = encoderValue.EncodeMsgpack(encoder) + } else { + err = encoder.Encode(&o.value) + } + + return newEncodeGenericError[T](err) +} + +// convertToDecoder checks whether the given value implements msgpack.CustomDecoder. +// +// Used internally during decoding to support custom MessagePack decoding logic. +func convertToDecoder(v interface{}) (msgpack.CustomDecoder, bool) { + dec, ok := v.(msgpack.CustomDecoder) + return dec, ok +} + +// DecodeMsgpack implements the msgpack.CustomDecoder interface. +// +// It reads a MessagePack value and decodes it into the Generic. +// - If the encoded value is nil, the optional is set to None. +// - Otherwise, it decodes into the internal value, using a custom decoder if available, +// and marks the optional as Some. +// +// Note: This method modifies the receiver and must be called on a pointer. +func (o *Generic[T]) DecodeMsgpack(decoder *msgpack.Decoder) error { + code, err := decoder.PeekCode() + switch { + case err != nil: + return newDecodeGenericError[T](err) + case code == msgpcode.Nil: + o.exists = false + + err := decoder.Skip() + if err != nil { + return newDecodeGenericError[T](err) + } + + return nil + } + + decoderValue, ok := convertToDecoder(&o.value) + if ok { + err = decoderValue.DecodeMsgpack(decoder) + } else { + err = decoder.Decode(&o.value) + } + + if err != nil { + return newDecodeGenericError[T](err) + } + + o.exists = true + + return nil +} diff --git a/generic_test.go b/generic_test.go new file mode 100644 index 0000000..24e1d46 --- /dev/null +++ b/generic_test.go @@ -0,0 +1,281 @@ +package option_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/vmihailenco/msgpack/v5" + + "github.com/tarantool/go-option" +) + +// TestSomeAndIsSome verifies that Some() creates a valid optional and IsSome returns true. +func TestSomeAndIsSome(t *testing.T) { + t.Parallel() + + opt := option.Some(42) + assert.True(t, opt.IsSome()) + assert.False(t, opt.IsZero()) + assert.False(t, opt.IsNil()) +} + +// TestNoneAndIsZero verifies that None() creates an empty optional and IsZero/IsNil returns true. +func TestNoneAndIsZero(t *testing.T) { + t.Parallel() + + opt := option.None[int]() + assert.True(t, opt.IsZero()) + assert.True(t, opt.IsNil()) + assert.False(t, opt.IsSome()) +} + +// TestZeroValueIsZero verifies that the zero value of Generic[T] behaves as None. +func TestZeroValueIsZero(t *testing.T) { + t.Parallel() + + var opt option.Generic[string] + assert.True(t, opt.IsZero()) + assert.True(t, opt.IsNil()) + assert.False(t, opt.IsSome()) +} + +// TestGetWithValue verifies Get returns the value and true when present. +func TestGetWithValue(t *testing.T) { + t.Parallel() + + opt := option.Some("hello") + value, ok := opt.Get() + assert.True(t, ok) + assert.Equal(t, "hello", value) +} + +// TestGetWithoutValue verifies Get returns zero value and false when absent. +func TestGetWithoutValue(t *testing.T) { + t.Parallel() + + opt := option.None[float64]() + value, ok := opt.Get() + assert.False(t, ok) + assert.InDelta(t, 0.0, value, 1e-6) // Zero value of float64. +} + +// TestMustGetWithValue verifies MustGet returns the value when present. +func TestMustGetWithValue(t *testing.T) { + t.Parallel() + + opt := option.Some(99) + value := opt.MustGet() + assert.Equal(t, 99, value) +} + +// TestMustGetPanic verifies MustGet panics when no value is present. +func TestMustGetPanic(t *testing.T) { + t.Parallel() + + opt := option.None[bool]() + assert.Panics(t, func() { opt.MustGet() }) //nolint:wsl_v5 +} + +// TestUnwrapAlias verifies Unwrap is an alias for MustGet. +func TestUnwrapAlias(t *testing.T) { + t.Parallel() + + opt := option.Some("test") + assert.Equal(t, "test", opt.Unwrap()) + + emptyOpt := option.None[string]() + assert.Empty(t, emptyOpt.Unwrap()) +} + +// TestUnwrapOrWithValue verifies UnwrapOr returns inner value when present. +func TestUnwrapOrWithValue(t *testing.T) { + t.Parallel() + + opt := option.Some("actual") + assert.Equal(t, "actual", opt.UnwrapOr("default")) +} + +// TestUnwrapOrWithoutValue verifies UnwrapOr returns default when absent. +func TestUnwrapOrWithoutValue(t *testing.T) { + t.Parallel() + + opt := option.None[string]() + assert.Equal(t, "default", opt.UnwrapOr("default")) +} + +// TestUnwrapOrElseWithValue verifies UnwrapOrElse returns inner value when present. +func TestUnwrapOrElseWithValue(t *testing.T) { + t.Parallel() + + opt := option.Some(100) + result := opt.UnwrapOrElse(func() int { + return 200 // Should not be called. + }) + assert.Equal(t, 100, result) +} + +// TestUnwrapOrElseWithoutValue verifies UnwrapOrElse calls func when absent. +func TestUnwrapOrElseWithoutValue(t *testing.T) { + t.Parallel() + + var ( + called bool + opt = option.None[int]() + ) + + result := opt.UnwrapOrElse(func() int { + called = true + + return 42 + }) + + assert.True(t, called) + assert.Equal(t, 42, result) +} + +// TestMsgpackEncodeSome verifies that a Some value is correctly encoded to MessagePack. +func TestMsgpackEncodeSome(t *testing.T) { + t.Parallel() + + opt := option.Some("hello") + + data, err := msgpack.Marshal(opt) + require.NoError(t, err) + + var decoded string + + err = msgpack.Unmarshal(data, &decoded) + require.NoError(t, err) + assert.Equal(t, "hello", decoded) +} + +// TestMsgpackEncodeNone verifies that a None value is encoded as nil in MessagePack. +func TestMsgpackEncodeNone(t *testing.T) { + t.Parallel() + + opt := option.None[string]() + + data, err := msgpack.Marshal(opt) + require.NoError(t, err) + + var result *string + + err = msgpack.Unmarshal(data, &result) + require.NoError(t, err) + assert.Nil(t, result) +} + +// TestMsgpackDecodeSome verifies that MessagePack data can be decoded into a Some optional. +func TestMsgpackDecodeSome(t *testing.T) { + t.Parallel() + + data, _ := msgpack.Marshal("hello") + + var opt option.Generic[string] + + err := msgpack.Unmarshal(data, &opt) + require.NoError(t, err) + + assert.True(t, opt.IsSome()) + assert.Equal(t, "hello", opt.Unwrap()) +} + +// TestMsgpackDecodeNone verifies that nil MessagePack data becomes a None optional. +func TestMsgpackDecodeNone(t *testing.T) { + t.Parallel() + + data, _ := msgpack.Marshal(nil) + + var opt option.Generic[int] + + err := msgpack.Unmarshal(data, &opt) + require.NoError(t, err) + + assert.True(t, opt.IsZero()) + assert.True(t, opt.IsNil()) + assert.False(t, opt.IsSome()) +} + +// TestRoundTrip verifies full encode-decode roundtrip for both Some and None. +func TestRoundTrip(t *testing.T) { + t.Parallel() + + // Test roundtrip of Some. + originalSome := option.Some("roundtrip-test") + data, err := msgpack.Marshal(originalSome) + require.NoError(t, err) + + var decodedSome option.Generic[string] + + err = msgpack.Unmarshal(data, &decodedSome) + require.NoError(t, err) + assert.True(t, decodedSome.IsSome()) + assert.Equal(t, "roundtrip-test", decodedSome.Unwrap()) + + // Test roundtrip of None. + originalNone := option.None[string]() + + data, err = msgpack.Marshal(originalNone) + require.NoError(t, err) + + var decodedNone option.Generic[string] + + err = msgpack.Unmarshal(data, &decodedNone) + require.NoError(t, err) + assert.True(t, decodedNone.IsZero()) +} + +// TestCustomEncoderDecoder demonstrates support for types with custom (de)serialization. +type CustomType struct { + Value string +} + +func (c *CustomType) EncodeMsgpack(enc *msgpack.Encoder) error { + return enc.EncodeString("custom:" + c.Value) +} + +func (c *CustomType) DecodeMsgpack(dec *msgpack.Decoder) error { + s, err := dec.DecodeString() + if err != nil { + return err + } + + c.Value = s[7:] // Strip "custom:". + + return nil +} + +func TestCustomEncoderDecoder(t *testing.T) { + t.Parallel() + + opt := option.Some(CustomType{Value: "test"}) + + data, err := msgpack.Marshal(opt) + require.NoError(t, err) + + var decodedOpt option.Generic[CustomType] + + err = msgpack.Unmarshal(data, &decodedOpt) + require.NoError(t, err) + + assert.True(t, decodedOpt.IsSome()) + assert.Equal(t, "test", decodedOpt.Unwrap().Value) +} + +// TestUnwrapOrElseLazyEvaluation verifies that the default function is not called unnecessarily. +func TestUnwrapOrElseLazyEvaluation(t *testing.T) { + t.Parallel() + + opt := option.Some(10) + + var called bool + + result := opt.UnwrapOrElse(func() int { + called = true + return 999 + }) + + assert.Equal(t, 10, result) + assert.False(t, called, "default function should not be called when value exists") +} diff --git a/int16_gen.go b/int16_gen.go index ea0e7c3..e578b00 100644 --- a/int16_gen.go +++ b/int16_gen.go @@ -14,6 +14,8 @@ type Int16 struct { exists bool } +var _ commonInterface[int16] = (*Int16)(nil) + // SomeInt16 creates an optional Int16 with the given int16 value. // The returned Int16 will have IsSome() == true and IsZero() == false. // diff --git a/int32_gen.go b/int32_gen.go index 8256dfa..e2d4a11 100644 --- a/int32_gen.go +++ b/int32_gen.go @@ -14,6 +14,8 @@ type Int32 struct { exists bool } +var _ commonInterface[int32] = (*Int32)(nil) + // SomeInt32 creates an optional Int32 with the given int32 value. // The returned Int32 will have IsSome() == true and IsZero() == false. // diff --git a/int64_gen.go b/int64_gen.go index c98a34a..7c800ef 100644 --- a/int64_gen.go +++ b/int64_gen.go @@ -14,6 +14,8 @@ type Int64 struct { exists bool } +var _ commonInterface[int64] = (*Int64)(nil) + // SomeInt64 creates an optional Int64 with the given int64 value. // The returned Int64 will have IsSome() == true and IsZero() == false. // diff --git a/int8_gen.go b/int8_gen.go index 6e661cc..ede0695 100644 --- a/int8_gen.go +++ b/int8_gen.go @@ -14,6 +14,8 @@ type Int8 struct { exists bool } +var _ commonInterface[int8] = (*Int8)(nil) + // SomeInt8 creates an optional Int8 with the given int8 value. // The returned Int8 will have IsSome() == true and IsZero() == false. // diff --git a/int_gen.go b/int_gen.go index ad9e97d..5fa8504 100644 --- a/int_gen.go +++ b/int_gen.go @@ -14,6 +14,8 @@ type Int struct { exists bool } +var _ commonInterface[int] = (*Int)(nil) + // SomeInt creates an optional Int with the given int value. // The returned Int will have IsSome() == true and IsZero() == false. // diff --git a/interface.go b/interface.go new file mode 100644 index 0000000..0991ed5 --- /dev/null +++ b/interface.go @@ -0,0 +1,20 @@ +package option + +import ( + "github.com/vmihailenco/msgpack/v5" +) + +// commonInterface is the interface that must be implemented by all optional types (generated and hand-written). +type commonInterface[T any] interface { + IsSome() bool + IsZero() bool + IsNil() bool + Get() (T, bool) + MustGet() T + Unwrap() T + UnwrapOr(def T) T + UnwrapOrElse(defCb func() T) T + + EncodeMsgpack(enc *msgpack.Encoder) error + DecodeMsgpack(dec *msgpack.Decoder) error +} diff --git a/string_gen.go b/string_gen.go index 2e50eb0..cd7ab14 100644 --- a/string_gen.go +++ b/string_gen.go @@ -14,6 +14,8 @@ type String struct { exists bool } +var _ commonInterface[string] = (*String)(nil) + // SomeString creates an optional String with the given string value. // The returned String will have IsSome() == true and IsZero() == false. // diff --git a/uint16_gen.go b/uint16_gen.go index 30048a6..3e99de9 100644 --- a/uint16_gen.go +++ b/uint16_gen.go @@ -14,6 +14,8 @@ type Uint16 struct { exists bool } +var _ commonInterface[uint16] = (*Uint16)(nil) + // SomeUint16 creates an optional Uint16 with the given uint16 value. // The returned Uint16 will have IsSome() == true and IsZero() == false. // diff --git a/uint32_gen.go b/uint32_gen.go index 85c7626..3fdee12 100644 --- a/uint32_gen.go +++ b/uint32_gen.go @@ -14,6 +14,8 @@ type Uint32 struct { exists bool } +var _ commonInterface[uint32] = (*Uint32)(nil) + // SomeUint32 creates an optional Uint32 with the given uint32 value. // The returned Uint32 will have IsSome() == true and IsZero() == false. // diff --git a/uint64_gen.go b/uint64_gen.go index 093ff6d..91264da 100644 --- a/uint64_gen.go +++ b/uint64_gen.go @@ -14,6 +14,8 @@ type Uint64 struct { exists bool } +var _ commonInterface[uint64] = (*Uint64)(nil) + // SomeUint64 creates an optional Uint64 with the given uint64 value. // The returned Uint64 will have IsSome() == true and IsZero() == false. // diff --git a/uint8_gen.go b/uint8_gen.go index 2d5882c..dfc338d 100644 --- a/uint8_gen.go +++ b/uint8_gen.go @@ -14,6 +14,8 @@ type Uint8 struct { exists bool } +var _ commonInterface[uint8] = (*Uint8)(nil) + // SomeUint8 creates an optional Uint8 with the given uint8 value. // The returned Uint8 will have IsSome() == true and IsZero() == false. // diff --git a/uint_gen.go b/uint_gen.go index adcd4c5..53c95aa 100644 --- a/uint_gen.go +++ b/uint_gen.go @@ -14,6 +14,8 @@ type Uint struct { exists bool } +var _ commonInterface[uint] = (*Uint)(nil) + // SomeUint creates an optional Uint with the given uint value. // The returned Uint will have IsSome() == true and IsZero() == false. //