Go stdlib extensions — admitted only when the standard library doesn't offer it.
The long-term ambition is the slot Guava fills in Java: the utility layer a project reaches for before hand-writing a helper. Go's ecosystem splits that slot today — samber/lo owns generic collection transforms, gods owns data structures, lancet goes kitchen-sink — and go-stdx competes on discipline, not breadth: stdlib-mirror naming, and only the small, common data operations every service would otherwise re-wrap.
The name is the admission rule: a hand-rolled max, a slices.Clone re-implementation, or a strconv wrapper does not belong here — use stdlib. What earns a slot is the three-to-five-line wrapper real projects keep re-writing because the stdlib deliberately omits it. Generating the underlying data (a UUID, a hash) is not our job — we lean on a mature library for that and wrap only the shape callers pass around.
Subpackages mirror stdlib naming so call sites read like the standard library they extend:
| package | what | why not stdlib / a lib |
|---|---|---|
slicesx |
Uniq, UniqBy — first-occurrence, order-preserving dedup |
the "seen map" loop everyone re-writes; stdlib's slices has no transform family |
stringsx |
Truncate / TruncateEllipsis — byte-budget cuts; FirstNonBlank; SplitAndTrim |
the log-truncation and human-written-list helpers every service re-writes (cmp.Or covers non-empty, not non-blank) |
osx |
EnvStr / EnvBool / EnvInt / EnvInt64 / EnvDuration — typed env lookups with defaults; WriteFileAtomic — temp+rename write |
config-from-env boilerplate in every service; os.WriteFile can leave readers a torn file |
ptrx |
To / Value / FormatOr — optional-field pointer helpers |
no stdlib answer to &literal (k8s.io/utils/ptr exists because of the gap) |
filepathx |
DirBytes — recursive regular-file byte count |
the quota/GC accounting walk everyone re-writes |
tarx |
PackDir / UnpackDir — directory ⇄ tar.gz with zip-slip defense |
archive/tar leaves both loops to the caller, and the extraction loop is famously easy to get wrong |
shellx |
Quote — POSIX single-quoting |
Go has no shlex; unquoted interpolation into a shell line is an injection |
randx |
Hex(n) — n random bytes as lowercase hex |
the "short random id" helper every daemon re-writes |
uuid |
V4, V7, V7Hex — random / time-ordered ids |
thin wrappers over google/uuid for the string / dashless-hex shapes services keep re-wrapping |
Rules of the house:
- Don't reinvent the data, wrap the shape: no chasing zero dependencies — a mature foundational library (
google/uuid, …) is a fine dependency. What we add is the small wrapper around it, not a re-implementation of it. - stdlib-first: when Go's standard library grows an equivalent, the entry here is deprecated and removed.
- Positioning is the bar: anything genuinely generic that projects would otherwise hand-write belongs here — no waiting for N copies to accumulate first.
import (
"github.com/qiankunli/go-stdx/osx"
"github.com/qiankunli/go-stdx/randx"
"github.com/qiankunli/go-stdx/slicesx"
)
port := osx.EnvInt("APP_PORT", 8080)
id := "job-" + randx.Hex(6)
ids := slicesx.Uniq(rawIDs)Used by case-code-review, hostel, and other Go projects under this account.
MIT