This guide covers the non-obvious parts of Nimony's standard library — behavioral contracts, performance guidance, and design decisions that aren't apparent from reading the type signatures alone.
For complete API details, read the source directly. For usage patterns,
see the examples directory (run them with hastur examples).
The system module is always imported implicitly.
string and seq use value semantics. Assignment copies; strings
are copy-on-write internally.
-
beginStore/endStore: The safe way to do bulk writes into a string through a pointer.beginStore(s, len, start)returns aptr UncheckedArray[char]pointing at positionstart; you may write up tolenbytes through it. You must callendStore(s)when done.Why
endStoreis required: Nim strings use Small String Optimization. Long strings store their data on the heap, but the first bytes are cached inline in the string object for fast access. After a bulk write through the heap pointer, this inline cache is stale.endStoresyncs it back. ForgettingendStoremeans subsequent reads via normals[i]return wrong data for the first characters.beginStorealso handles copy-on-write (ensures the heap buffer is unique before returning a mutable pointer).var s = newString(100) let p = beginStore(s, 5) # get pointer, ensure unique p[0] = 'h' p[1] = 'e' p[2] = 'l' p[3] = 'l' p[4] = 'o' endStore(s) # sync inline cache — do not forget!
The older
prepareMutationonly handles copy-on-write but does not sync the inline cache, so it is insufficient with SSO. PreferbeginStore/endStore. -
del: Removes an element from aseqby swapping it with the last element. O(1) but changes order. If you need stable order, shift elements manually. -
grow/shrinkare preferred oversetLen— they are more explicit about direction.growtakes a target length and a value to initialize new elements with:s.grow(newLen, val).growUnsafeskips initialization; use with care.
- Implement only
==,<=and<for your types.!=,>,>=are generic templates that derive from those two. is/isnotare compile-time type checks.ofis a runtime subtype check (for object inheritance).cmpreturnsint(negative, zero, positive) — use it for sorting, not for equality testing.
swapexchanges bits directly. It does not call=sink,=copy, or=duphooks. This is by design — it's faster this way and Nim's lifetime tracking hooks do not support "self-pointers" (pointers inside the same object,obj.field = addr(obj)).allocFixed/deallocFixed: Low-level fixed-size allocation.setOomHandler: By default the runtime tries to continue after out-of-memory. For many applications, just quitting is the more robust solution — set a handler that callsquit.
..is inclusive:0..3yields 0, 1, 2, 3...<is exclusive on the right:0..<3yields 0, 1, 2.
The system module defines Continuation, ContinuationProc,
CoroutineBase, and Scheduler for continuation-based concurrency.
See the CPS documentation for details.
Synchronous file I/O. echo, stdin, stdout, stderr are always
available.
openreturns bool**, not a File — check it. TheFileis an out-parameter.readLinereturnsfalseat EOF. Use it in awhileloop.writeLineappends the platform line ending.tryWriteFileis a convenience that opens, writes, and closes in one call.quit(msg)writes to stderr and exits — useful for fatal errors.
ASCII string operations. For Unicode, use unicode instead.
The module exports const sets: Whitespace, Letters, Digits,
HexDigits, IdentChars, IdentStartChars, Newlines,
PrintableChars, AllChars, and subsets like UppercaseLetters /
LowercaseLetters / PunctuationChars.
Use AllChars - ValidChars to build an invalid character set for
find.
cmpIgnoreCasereturnsint(notbool) — designed for sort comparators.cmpIgnoreStyleignores case and underscores, matching Nim's identifier comparison rules:foo_bar==FooBar.normalizelowercases and strips underscores — the canonical form for Nim-style comparison.
split with set[char] treats each character as an independent
separator. Adjacent separators produce empty strings in the output.
Use maxsplit to limit the number of splits.
findreturns-1on no match, not an exception.replacereturns a new string; it does not mutate.replaceWordonly matches whole words (checks word boundaries).multiReplaceapplies multiple replacements in a single pass.
%interpolates$1,$2, ... placeholders.formatis the same thing as a named proc.formatFloat/formatBiggestFloat: UseffDecimalfor fixed decimals,ffScientificfor scientific notation.formatSizeformats byte counts as human-readable strings (KiB, MiB, etc.).
Full Unicode support. Operates on Rune (a single code point).
Key distinction: byte position vs rune position. Most procs
take byte positions for efficiency. Use runeOffset to convert
a rune index to a byte index.
runeLenreturns the number of runes (not bytes).s.lengives bytes.fastRuneAtis a template that avoids allocation — use it in tight loops instead ofruneAt.graphemeLenaccounts for combining characters — one "visible character" may be multiple runes.toLower/toUpperhere handle the full Unicode range, unlikestrutils.toLowerAscii.validateUtf8returns -1 if valid, otherwise the byte position of the first invalid byte.
Hash values are prerequisites for Table and HashSet keys.
- Combine hashes with
!&, finalize with!$. - The
Hashableconcept describes what a type needs to be hashable. hashIgnoreStylematchescmpIgnoreStyle— use them together for Nim-style identifier tables.
Generic hash tables. Keys must satisfy Keyable (have == and hash).
You must import std/hashes alongside std/tables to make the hash
procs visible — tables does not re-export them.
The table uses a hybrid strategy: linear scan for ≤ 4 entries, hash table with open addressing for larger sizes. This means small tables have no hashing overhead at all.
[]raisesKeyErroron missing keys. UsegetOrDefaultfor a safe fallback, orcontainsto check first.mgetOrPutreturns avarreference — ideal for counters:tab.mgetOrPut(key, 0) += 1.getOrQuitis for cases where a missing key is a programming error — it callsquit, not an exception.
Hash-based sets for any hashable type. Built on top of Table[T, bool].
Like tables, you must also import std/hashes.
containsOrInclis the deduplication primitive — returns whether the element was already present, and adds it if not.
For small ordinal types (char, enum, small integer ranges), prefer
the built-in set[T] which is a bitset and much faster.
Type-safe path handling via the Path distinct type.
/joins paths and normalizes. Use it instead of string concatenation.splitFilereturns(dir, name, ext)whereextincludes the leading dot. Empty components are empty strings, never/.relativePathcan use'/'as separator (viasepparameter) for URL construction.absolutePathdefaults to the current directory as root.expandTildehandles~/expansion. Works on all platforms including Windows.
Directory operations.
createDircreates nested directories (likemkdir -p). No error if it already exists.walkDiryields(kind, path)tuples. Thekinddistinguishes files, directories, and symlinks. Setrelative = trueto get just names instead of full paths.tryRemoveFinalDir/tryRemoveFilereturn OS error codes instead of raising — useful when you need to distinguish "not found" from "permission denied".
getEnvreturns""for missing variables. UseexistsEnvto distinguish missing from empty.envPairsiterates all environment variables as(key, value).
Platform-correct application directories (XDG on Linux, Library/ on
macOS, AppData on Windows).
getConfigDir/getDataDir/getCacheDirrespectXDG_*environment variables on Unix.getTempDirdoes not verify the directory exists.
Memory-mapped file I/O. The MemFile type exposes mem (pointer)
and size.
openwithmappedSize = -1maps the entire file.- The
offsetparameter must be a multiple of the OS page size. closeflushes changes for files opened with write access.
Character encoding conversion. Uses iconv on Unix, Windows API on Windows.
getCurrentEncodingalways returns"UTF-8"on Unix.- Open a converter with
open(srcEncoding, destEncoding), then callconvert. Don't forget tocloseit.
wrapWordshandles Unicode graphemes correctly.- Set
splitLongWords = falseto keep long words intact instead of breaking them mid-word.
Low-level thread creation.
createtakes afn(arg)pair and an optional stack size (0 = system default, typically 2MB).pinnedToCpuis best-effort — some platforms (macOS) do not support CPU pinning.- Always
jointhreads before the program exits.