Typed temporary-object reuse for Go hot paths.
Quick Start · Core Model · Intended Use · Docs Index · Contributing · Security · Performance
Policy-driven reuse · Explicit ownership transfer · Canonical return-path semantics · Benchmark-first engineering
Read by goal: Start here · Lifecycle semantics · Architecture · Non-goals · Performance evidence
README.md is the public landing page. docs/index.md is the
repository documentation index, and doc.go remains the package
contract for Go users and pkg.go.dev.
arcoris.dev/pool is a small Go package for typed reuse of temporary mutable
values. It stays close to the sync.Pool mental model, but keeps construction,
reset, reuse admission, and drop observation explicit as policy callbacks
instead of turning temporary-object reuse into a larger framework.
| If you want to... | Read |
|---|---|
| understand the package in one screen | Quick start and Core model |
| learn the exact ownership and return-path contract | Lifecycle guide |
| understand repository structure and boundaries | Architecture guide |
| see what the package intentionally excludes | Non-goals guide |
| find contributor, reporting, or repository policy paths | Documentation |
| inspect benchmarks, charts, and curated reports | Performance overview |
In hot code paths, temporary mutable state usually ends up in one of two shapes:
- repeated fresh allocation of scratch objects; or
- direct
sync.Poolusage scattered through the codebase, with reset, admission, and drop logic repeated manually.
arcoris.dev/pool keeps the temporary-object model close to sync.Pool, but
makes the lifecycle policy explicit in one place:
Newdefines how values are constructed on a miss;Resetdefines how accepted values are cleaned before storage;Reusedecides whether a returned value is worth keeping;OnDropobserves explicit rejection by policy.
package main
import "arcoris.dev/pool"
type Builder struct {
buf []byte
}
func main() {
p := pool.New(pool.Options[*Builder]{
New: func() *Builder {
return &Builder{
buf: make([]byte, 0, 1024),
}
},
Reset: func(b *Builder) {
b.buf = b.buf[:0]
},
Reuse: func(b *Builder) bool {
return cap(b.buf) <= 1024
},
})
v := p.Get()
v.buf = append(v.buf, "hello"...)
// Ownership ends here. The caller must not use v after Put returns.
p.Put(v)
}The package is built around three layers:
Options[T]defines lifecycle policy;Pool[T]exposes the publicGet/Putruntime;- an internal backend stores already-clean reusable values.
The return path is canonical:
- evaluate reuse admission;
- if reuse is denied, invoke
OnDropand stop; - if reuse is allowed, invoke
Reset; - store the cleaned value in the backend.
This ordering is part of the package contract. Admission runs before reset so policy can inspect the real post-use state. Reset runs before backend storage so retained values stay clean while idle.
Ownership is explicit:
- after
Get, the caller owns the value; - after
Put, the caller must treat the value as no longer owned.
A value must not be used after Put returns.
Pool[T] itself is safe for concurrent use by multiple goroutines. That does
not make the borrowed value automatically safe for concurrent use. Any
concurrency properties of T remain the responsibility of the caller and the
type.
The package usually works best with pointer-like mutable values.
Why:
- the runtime avoids copying larger state around;
- reset can happen in place;
- the usage model aligns well with temporary-object pooling in Go;
- performance benefits are easier to justify.
Value types are supported, but large or frequently copied values should be benchmarked carefully. The repository benchmark layer already includes explicit pointer-like versus value-shape coverage.
This package is a good fit for:
- parser or decoder state;
- request-scoped scratch structures;
- builders, envelopes, and reusable work records;
- pointer-like temporary objects on hot paths.
It is usually worth considering when:
- values are created frequently;
- one borrowed value belongs to one logical operation at a time;
- reset is cheaper than repeated reconstruction;
- reuse policy benefits from being explicit and local.
arcoris.dev/pool is intentionally not:
- a general resource manager;
- a borrow or lease tracker;
- a stable object inventory or cache;
- a queue, scheduler, or semaphore;
- a validation framework;
- a specialized memory or buffer pool;
- a rich commons-pool-style framework.
The package also does not promise:
- stable retention of returned values;
- borrow tracking in the default runtime;
- zero allocations for every
T; - that pooling is beneficial for all shapes of
T.
| Document | Use when | Focus |
|---|---|---|
| Docs index | you want the repository map first | the best entry point into the documentation set |
| Package contract | you want the Go-facing package contract | exported API intent and runtime model |
| Contributing guide | you want contributor workflow and validation expectations | PR shape, validation, docs sync, and performance evidence rules |
| Security policy | you need the repository's vulnerability reporting and security scope guidance | private reporting path, supported versions, and repo-specific security boundaries |
| Code of Conduct | you want the repository's collaboration and moderation baseline | expected behavior, reporting path, and review standards |
| Third-Party Notices | you need attribution and third-party notice status | bundled or adapted upstream material and pinned tooling references |
| Architecture guide | you want the structural view | layers, boundaries, repository layout |
| Lifecycle guide | you need precise runtime semantics | ownership, acquisition, return-path invariants |
| Non-goals guide | you are evaluating scope | explicit exclusions and product boundaries |
| Performance overview | you want the evidence trail | benchmarks, charts, methodology, reports |
| Initial baseline report | you want the current curated snapshot | current benchmark interpretation and chart links |
The repository keeps benchmark, profile, chart, and report layers as first-class engineering artifacts. Snapshot charts are presentation summaries over raw benchmark evidence, not replacements for the underlying artifacts.
Use the performance overview for the full artifact workflow and the initial baseline report for the current curated snapshot.