box module
A box is a tiny mutable cell that holds a single value. It provides reference-like semantics without language-level pointers. Use it when you want to share and mutate a value across functions, or avoid copying large values.
- Import:
import box - Backing: A box is implemented as a one-element array internally.
- Pattern: Get/transform/Set via explicit functions.
API
box.New(value any) (box, error) - Create a new box containing
value. box.Get(b box) (value, error) - Read the value stored in the box.
box.Set(b box, value any) (None, error) - Replace the value in the box with
value.
All functions return (result, error) tuples consistent with Harneet stdlib conventions.
Why use a box?
- Pass-by-reference semantics without references:
- Let a callee update a caller’s value.
- Shared mutable state:
- Multiple functions can observe and update a single, shared slot.
- Large values:
- Pass a small handle (the box) rather than copying/rebinding large data repeatedly.
When to use in the real world
- Counters, accumulators, and state shared across helpers.
- Swapping values across two locations.
- Coordinating state across callbacks or closures.
- Managing large data held in memory (e.g., a full file’s contents) when a single shared value needs mutation.
- Note: For very large data processing, prefer streaming or chunked processing via the
file module if you don’t need the entire data in memory.
Examples
Basic usage
| package main
import fmt
import box
// Create a box holding an integer
var counterBox, err = box.New(0)
if err != None {
fmt.Println("box.New error:", err)
}
// Read
var value, err1 = box.Get(counterBox)
if err1 == None {
fmt.Println("initial value:", value)
}
// Write
var _, err2 = box.Set(counterBox, 42)
if err2 == None {
var newValue, _ = box.Get(counterBox)
fmt.Println("updated value:", newValue) // 42
}
|
Pass-by-reference style mutation
| package main
import fmt
import box
function Increment(b any) {
var v, err = box.Get(b)
if err != None {
fmt.Println("box.Get error:", err)
return
}
var _, setErr = box.Set(b, v + 1)
if setErr != None {
fmt.Println("box.Set error:", setErr)
return
}
}
var counter, e = box.New(10)
if e == None {
Increment(counter)
var v, _ = box.Get(counter)
fmt.Println("after increment:", v) // 11
}
|
Swap using boxes (shared mutable state)
| package main
import fmt
import box
function Swap(a any, b any) {
var av, errA = box.Get(a)
if errA != None { return }
var bv, errB = box.Get(b)
if errB != None { return }
var _, errSetA = box.Set(a, bv)
if errSetA != None { return }
var _, errSetB = box.Set(b, av)
if errSetB != None { return }
}
var x, _ = box.New(1)
var y, _ = box.New(2)
Swap(x, y)
var xv, _ = box.Get(x)
var yv, _ = box.Get(y)
fmt.Println("x:", xv, "y:", yv) // x: 2 y: 1
|
Large data in a box
| package main
import fmt
import box
import file
import strings
// Read a full file (for demo). For very large files, consider streaming.
var content, readErr = file.Read("README.md")
if readErr == None {
var contentBox, err = box.New(content)
if err == None {
var current, _ = box.Get(contentBox)
var updated = current + "\n---\nprocessed by box example\n"
var _, setErr = box.Set(contentBox, updated)
if setErr == None {
var final, _ = box.Get(contentBox)
var length, lenErr = strings.Len(final)
if lenErr == None {
fmt.Println("content length:", length)
}
}
}
}
|
Remember this →
- Treat a box like a tiny heap-allocated cell holding one value.
- Don’t overuse boxes; prefer pure functions and returning updated values when mutation isn’t needed.
- Boxes are excellent when you need a shared, mutable slot or pass-by-reference semantics.