Skip to content

Commit

Permalink
maybe: godoc, tests, example
Browse files Browse the repository at this point in the history
  • Loading branch information
sh0rez committed Apr 3, 2024
1 parent e27c8f7 commit ecc88a8
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 25 deletions.
25 changes: 0 additions & 25 deletions processor/deltatocumulativeprocessor/internal/maybe/option.go

This file was deleted.

52 changes: 52 additions & 0 deletions processor/deltatocumulativeprocessor/internal/maybe/ptr.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

// maybe provides utilities for representing data may or may not exist at
// runtime in a safe way.
//
// A typical approach to this are pointers, but they suffer from two issues:
// - Unsafety: permitting nil pointers must require careful checking on each use,
// which is easily forgotten
// - Blindness: nil itself does cannot differentiate between "set to nil" and
// "not set all", leading to unexepcted edge cases
//
// The [Ptr] type of this package provides a safe alternative with a clear
// distinction between "not set" and "set to nil".
package maybe // import "github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor/internal/maybe"

// Ptr references some value of type T that is not guaranteed to exist.
// Callers must use [Ptr.Try] to access the underlying value, checking the
// ok return value too.
// This provides a clear distinction between "not set" and "set to nil".
//
// Use [Some] and [None] to create Ptrs.
type Ptr[T any] struct {
to *T
ok bool
}

// None returns a Ptr that represents "not-set".
// This is equal to a zero-value Ptr.
func None[T any]() Ptr[T] {
return Ptr[T]{to: nil, ok: false}
}

// Some returns a pointer to the passed T.
//
// The ptr argument may be nil, in which case this represents "explictely set to
// nil".
func Some[T any](ptr *T) Ptr[T] {
return Ptr[T]{to: ptr, ok: true}
}

// Try attempts to de-reference the Ptr, giving one of three results:
//
// - nil, false: not-set
// - nil, true: explicitely set to nil
// - non-nil, true: set to some value
//
// This provides extra safety over bare pointers, because callers are forced by
// the compiler to either check or explicitely ignore the ok value.
func (ptr Ptr[T]) Try() (_ *T, ok bool) {
return ptr.to, ptr.ok
}
60 changes: 60 additions & 0 deletions processor/deltatocumulativeprocessor/internal/maybe/ptr_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package maybe_test

import (
"fmt"
"testing"

"github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor/internal/maybe"
"github.com/stretchr/testify/require"
)

func TestMaybe(t *testing.T) {
t.Run("zero-not-ok", func(t *testing.T) {
var ptr maybe.Ptr[int]
_, ok := ptr.Try()
require.False(t, ok)
})
t.Run("none-not-ok", func(t *testing.T) {
ptr := maybe.None[int]()
_, ok := ptr.Try()
require.False(t, ok)
})
t.Run("explicit-nil", func(t *testing.T) {
ptr := maybe.Some[int](nil)
v, ok := ptr.Try()
require.Nil(t, v)
require.True(t, ok)
})
t.Run("value", func(t *testing.T) {
num := 42
ptr := maybe.Some(&num)
v, ok := ptr.Try()
require.True(t, ok)
require.Equal(t, num, *v)
})
}

func ExamplePtr() {
var unset maybe.Ptr[int] // = maybe.None()
if v, ok := unset.Try(); ok {
fmt.Println("unset:", v)
} else {
fmt.Println("unset: !ok")
}

var xnil maybe.Ptr[int] = maybe.Some[int](nil)
if v, ok := xnil.Try(); ok {
fmt.Println("explicit nil:", v)
}

num := 42
var set maybe.Ptr[int] = maybe.Some(&num)
if v, ok := set.Try(); ok {
fmt.Println("set:", *v)
}

// Output:
// unset: !ok
// explicit nil: <nil>
// set: 42
}

0 comments on commit ecc88a8

Please sign in to comment.