From 42c5b5f8f4c1dd1df58f382ccb0cfde174e45c4c Mon Sep 17 00:00:00 2001 From: "M.V. Hutz" Date: Tue, 14 Apr 2026 01:58:15 +0000 Subject: [PATCH] feat!: update get from `(V, error)` to `(V, bool)` (#20) ## Description Currently, the signature for `Table.Get` is `func (K) (V, error)`. This is not very Go-idiomatic, which prefers to return a boolean instead of an error. For instance, a built-in Go map is used like so: ```go if value, ok := users[id]; !ok { // ... } ``` Updating our table to look like that is best practice. In that same vein, to support direct lookup (i.e. `v := users[id]`), this PR also adds `Table.Find`. ## Changes - BREAKING CHANGE: Update contract of `Table.Get` to `func (K) (V, bool)`. Returns 'false' is the item cannot be found, and 'true' if it is found. - Add `Table.Find`. - Updated tests and documentation to match the change. ### Design Decisions - Chose to make this decision because throwing an error implies that there is something 'wrong' with the table. There is nothing wrong with the table; it is just that the item does not exist. ## Checklist - [x] Tests pass - [x] Docs updated Reviewed-on: https://git.maximhutz.com/tools/go-cuckoo/pulls/20 Co-authored-by: M.V. Hutz Co-committed-by: M.V. Hutz --- cuckoo_fuzz_test.go | 8 ++++---- cuckoo_test.go | 6 +++--- doc_example_test.go | 10 +++++----- table.go | 23 +++++++++++++++-------- 4 files changed, 27 insertions(+), 20 deletions(-) diff --git a/cuckoo_fuzz_test.go b/cuckoo_fuzz_test.go index f849de0..76bb815 100644 --- a/cuckoo_fuzz_test.go +++ b/cuckoo_fuzz_test.go @@ -73,16 +73,16 @@ func FuzzInsertLookup(f *testing.F) { delete(expected, step.key) - _, err = actual.Get(step.key) - assert.Error(err) + _, ok := actual.Get(step.key) + assert.False(ok) } else { err := actual.Put(step.key, step.value) assert.NoError(err) expected[step.key] = step.value - found, err := actual.Get(step.key) - assert.NoError(err) + found, ok := actual.Get(step.key) + assert.True(ok) assert.Equal(step.value, found) } diff --git a/cuckoo_test.go b/cuckoo_test.go index 131194a..56afcde 100644 --- a/cuckoo_test.go +++ b/cuckoo_test.go @@ -108,12 +108,12 @@ func TestGetMany(t *testing.T) { } for i := range 2_000 { - value, err := table.Get(i) + value, ok := table.Get(i) if i < 1_000 { - assert.NoError(err) + assert.True(ok) assert.Equal(value, true) } else { - assert.Error(err) + assert.False(ok) } } } diff --git a/doc_example_test.go b/doc_example_test.go index 1fad4ff..da8d62a 100644 --- a/doc_example_test.go +++ b/doc_example_test.go @@ -14,19 +14,19 @@ func Example_basic() { fmt.Println("Put error:", err) } - if item, err := table.Get(1); err != nil { - fmt.Println("Error:", err) + if item, ok := table.Get(1); !ok { + fmt.Println("Not Found 1!") } else { fmt.Println("Found 1:", item) } - if item, err := table.Get(0); err != nil { - fmt.Println("Error:", err) + if item, ok := table.Get(0); !ok { + fmt.Println("Not Found 0!") } else { fmt.Println("Found 0:", item) } // Output: // Found 1: Hello, World! - // Error: key '0' not found + // Not Found 0! } diff --git a/table.go b/table.go index 52e1a69..8b4967d 100644 --- a/table.go +++ b/table.go @@ -95,24 +95,31 @@ func (t *Table[K, V]) shrink() error { return t.resize(t.bucketA.capacity / t.growthFactor) } -// Get fetches the value for a key in the [Table]. Returns an error if no value -// is found. -func (t Table[K, V]) Get(key K) (value V, err error) { +// Get fetches the value for a key in the [Table]. Matches the comma-ok pattern +// of a builtin map; see [Table.Find] for plain indexing. +func (t Table[K, V]) Get(key K) (value V, ok bool) { if item, ok := t.bucketA.get(key); ok { - return item, nil + return item, true } if item, ok := t.bucketB.get(key); ok { - return item, nil + return item, true } - return value, fmt.Errorf("key '%v' not found", key) + return +} + +// Find fetches the value of a key. Matches direct indexing of a builtin map; +// see [Table.Get] for a comma-ok pattern. +func (t Table[K, V]) Find(key K) (value V) { + value, _ = t.Get(key) + return } // Has returns true if a key has a value in the table. func (t Table[K, V]) Has(key K) (exists bool) { - _, err := t.Get(key) - return err == nil + _, exists = t.Get(key) + return } // Put sets the value for a key. Returns error if its value cannot be set.