Nim — Reference
Source: https://nim-lang.org/documentation.html
Nim
- Created: design started 2005 by Andreas Rumpf (originally “Nimrod”); first public release 2008; renamed to Nim in 2014 (Wikipedia).
- Latest stable: Nim 2.2.10, released 2026-04-24 (nim-lang.org).
- Owner: Nim Language Foundation; lead developer Andreas Rumpf. License: MIT.
- Paradigms: multi-paradigm — imperative, functional, generic, OO, concurrent, metaprogramming-heavy.
- Typing: static, strong, with bidirectional type inference. Nominal subtyping for
object/ref objecttypes. - Memory: choice of memory management modes —
--mm:orc(default since 2.0, cycle-collecting ARC),--mm:arc,--mm:refc(legacy default),--mm:markAndSweep,--mm:boehm,--mm:go,--mm:none. - Compilation: AOT — compiles to C (default), C++, Objective-C, or JavaScript. The C compiler then emits the native binary.
- Primary domains: systems programming, scripting, web (frontend via JS backend + backend via C), embedded, scientific computing, game dev, CLI tools.
- Official docs: https://nim-lang.org/documentation.html
At a glance
Nim is “Python-like syntax that compiles to fast C.” Indentation-based, statically typed, with a metaprogramming system (templates, generics, macros) so powerful that much of the stdlib is implemented as macros. The C-backend approach means Nim binaries are small, fast, easy to embed, and trivially portable to anywhere with a C compiler. ARC/ORC (since 2.0) gave Nim deterministic, cycle-aware reference counting that competes with Rust’s ownership model in throughput while keeping the GC veneer.
Getting started
Install:
choosenimis the official version manager (therustupof Nim):curl https://nim-lang.org/choosenim/init.sh -sSf | sh.- Or distro packages (
brew install nim,pacman -S nim,apt install nim). - Nim depends on a working C compiler (gcc, clang, or MSVC).
Hello world (file hello.nim):
echo "Hello, world!"Compile + run: nim c -r hello.nim. (c selects C backend; r runs after compile.) Other backends: nim cpp, nim js, nim objc.
Project layout (a Nimble package):
mypkg/
mypkg.nimble # package manifest
src/
mypkg.nim # entry point (= module name)
mypkg/
submod.nim
tests/
test1.nim
README.md
Package/build tool: Nimble (nimble). nimble init creates the scaffold, nimble install <pkg> installs (from https://nimble.directory/), nimble build / nimble test / nimble publish. The .nimble file is itself a Nim script, so build steps are arbitrary Nim code.
REPL: there is no first-class REPL. inim is the popular community REPL; nim secret (deprecated) was a primitive built-in. The modern interactive workflow is nim c -r with editor save-on-write.
Basics
Types and literals:
- Integers:
int(platform-sized),int8/16/32/64,uint,uint8...64,byte(=uint8). - Floats:
float(=float64),float32,float64. bool,char(single byte, not Unicode codepoint),string(mutable, UTF-8 by convention but bytewise indexed),cstring(null-terminated, FFI).- Containers:
array[N, T](fixed-length),seq[T](growable),set[T](bit-set, T must be ordinal),Table[K, V](intablesmodule). enum,range[a..b],tuple(anonymous structural —(name: string, age: int)),object(nominal, value-type),ref object(heap-allocated, GC-managed).- Distinct types:
type Meters = distinct floatfor newtype-style separation.
Variables/scoping: let (immutable single-assignment), var (mutable), const (compile-time constant). Block-scoped; block: ... for explicit. Two indentation levels make a new scope.
Control flow: if/elif/else, case ... of: (exhaustive on enums), for x in iter, while, block name: + break name, continue, return, try/except/finally. Everything is an expression: let x = if cond: 1 else: 2.
Functions:
proc add(x, y: int): int = x + y # last expr is result
proc greet(name = "world"): string =
"Hello, " & name
# Method-call syntax: x.add(y) == add(x, y) ("uniform function call")
echo 2.add(3) # 5
echo "abc".len # 3 (calls len("abc"))Variants: proc (regular), func ({.noSideEffect.} proc, no I/O / globals), method (dynamic dispatch on ref object), iterator (yields), template (hygienic AST sub), macro (AST manipulation).
Strings: mutable, length-prefixed, UTF-8 by convention. Concat with &. Interpolation via the strformat macro: fmt"x = {x+1}" or &"x = {x+1}". Indexing s[0] returns a byte (char), not a Unicode rune; use unicode module for rune iteration.
Collections: seq is the dynamic array (@[1, 2, 3] literal). array[3, int] for fixed. Table and OrderedTable from tables. HashSet from sets. Deque from deques. The [] operator is overloadable.
Intermediate
Type system depth:
- Generics:
proc f[T](x: T): T = x. Constraints:proc f[T: SomeNumber](x: T). Type classes:concept(structural typing — duck-typed at compile time). - Object inheritance:
type Animal = ref object of RootObj(must descend fromRootObjto participate in OO). Methods (dynamic dispatch) declared withmethod; regularprocis statically resolved. - Variant objects (sum types):
case kind: Kind of A: aField; of B: bField. - Object construction:
Animal(name: "rex", legs: 4). - Generics specialize at compile time (monomorphization, like C++/Rust).
Modules: each .nim file is a module. import foo brings public symbols (those marked with *, e.g. proc bar*() = ...); from foo import bar; include foo is a textual include (rarely used, for split implementations).
Error handling: exceptions (with Nim’s own hierarchy under Exception, CatchableError, Defect). raise newException(IOError, "msg"). try/except IOError as e: .... {.raises: [IOError].} pragma documents and statically checks raised exceptions; {.raises: [].} proves no exceptions. Idiom: Result[T, E] (third-party results package) for error-as-value style à la Rust.
Concurrency primitives:
- Threads:
import threadpool; spawn f()(FlowVar future), or rawcreateThread. Each thread has its own GC heap by default; sharing requiresChannel[T]orptrtypes. - Async/await:
import asyncdispatch; proc fetch(): Future[string] {.async.} = .... Cooperative event-loop viaasyncdispatch. Thechronoslibrary is the modern alternative (used by Status/Nimbus Ethereum client) with nicer cancellation semantics. - Parallelism:
parallel:block +spawnfor safe data-parallel; OpenMP-like||operator fromthreading/atomics.
I/O: readFile, writeFile, open(f, fmRead). Streams via streams module. httpclient for HTTP, asynchttpserver for serving. db_sqlite, db_postgres, db_mysql for databases. json, parsecsv, parsexml, parsecfg in stdlib.
Stdlib highlights: strutils (string utilities), sequtils (map, filter, zip), strformat (fmt/&), tables/sets/deques/heapqueue, os/osproc/pathutils, math, random, times/monotimes, re (PCRE bindings), nre (alternative), unicode, algorithm (sort, binarySearch), httpclient, asyncdispatch/asyncnet/asynchttpserver, json, streams, marshal, db_*, unittest.
Advanced
Memory / GC modes (--mm: flag):
orc(default since 2.0): ARC + cycle collector. Deterministic, low pause, no stop-the-world; throughput competitive with manual.arc: pure reference counting, no cycle collector — must avoid cycles or leak. Smallest runtime; great for embedded/WASM.refc(legacy default pre-2.0): deferred reference counting + mark-and-sweep cycle collector.markAndSweep: simple mark-and-sweep.boehm: Boehm-Demers-Weiser conservative GC (link-time dependency).go: Go runtime’s GC.none: no GC — fully manual; types using GC become illegal.
ARC/ORC also brings move semantics: =copy, =sink, =destroy, =trace, =dup hooks let you implement value types like Rust’s Drop/Clone.
Concurrency deep dive: each thread has an isolated heap with --mm:refc (default for legacy multi-thread). With --mm:orc/arc, heaps may be shared with appropriate annotations. Channel[T] for thread-safe message passing. The chronos async library is the production choice for high-perf I/O.
FFI:
proc cprintf(fmt: cstring) {.importc: "printf", varargs, header: "<stdio.h>".}— call C directly.{.exportc.}to expose a Nim symbol with C name.{.cdecl.},{.stdcall.}for calling conventions.c2nimtool generates Nim wrappers from C headers.- JS backend:
{.importjs: "Math.sqrt(#)".}to call JS.
Reflection: rich macro-time. getType(), getTypeImpl(), typeof(x), astToStr(expr). The macros module provides full AST walking inside macros.
Performance tools:
nim c -d:release -d:danger— release mode (dangerstrips runtime checks).--profiler:on(built-in sampling profiler), or usevalgrind --tool=callgrindon the C output.--debugger:nativefor gdb/lldb.nim --gen:cppand feed to gprof.--nimcache:dirto inspect generated C code.--listCmdto see exactly the C compile invocation.
God mode
Three layers of metaprogramming — pick the lightest that does the job:
- Generics: monomorphized type-parameterized procs/types.
- Templates: hygienic AST substitution, expanded after parsing but before type-checking. Cheap, parameterized.
- Macros: arbitrary Nim code that runs at compile time, takes
NimNodeAST, returns AST.
# Template — like a macro but pattern-only
template times(n: int, body: untyped) =
for _ in 0 ..< n: body
3.times: echo "hi"
# Macro — full AST manipulation
import macros
macro debug(args: varargs[untyped]): untyped =
result = newStmtList()
for a in args:
result.add quote do:
echo astToStr(`a`), " = ", `a`
let x = 42
debug(x, x*2) # prints: x = 42 \n x*2 = 84Term-rewriting macros ({.rewrite.}) let you pattern-match expressions and substitute — used for peephole optimizations (a*1 -> a).
Compile-time function execution (CTFE): Nim runs ordinary Nim code at compile time. const x = expensiveCompute() — the value is baked in. The CTFE VM is more capable than most language’s constexpr; it can do file I/O via slurp("path") (file embed), HTTP via staticExec("curl ...").
Choosing memory mode at compile time: nim c --mm:arc --opt:speed --d:release .... For embedded: --mm:none --os:freertos --gc:none. For WASM: --cpu:wasm32 --mm:orc --d:release (or via nimwc toolchains).
Multiple backends from one source:
when defined(js): ... else: ...blocks branch on backend.nim js --out:app.js src/app.nim→ JavaScript output for the browser, Node, or React Native bridges.nim cppfor C++ (allows interop with C++ classes via{.importcpp.}).nim objcfor Objective-C / iOS.
Importc / FFI: {.importc.} + {.header: "<file.h>".} directly imports C declarations. {.passC: "-I..."} and {.passL: "-l..."} add to the C compile/link line.
Hot code reloading: --hotCodeReloading:on allows updating proc bodies in a running process (Linux/macOS, mostly used for game/UI dev).
UFCS (uniform function call syntax): x.f(y) is identical to f(x, y). Combined with method-style chaining and operator overloading, Nim supports DSL-heavy code (e.g., karax HTML DSL).
Idioms & style
- Naming:
camelCasefor procs and variables,PascalCasefor types,SCREAMING_SNAKEfor constants is allowed butPascalCaseis also common. Nim is case-insensitive and underscore-insensitive for identifiers (except first letter):myVar,myvar,my_varare the same. (Style: pickcamelCaseand stick.) - Formatter:
nimpretty(built-in, comes with the compiler). - Linter: no canonical separate linter;
nim checkdoes basic semantics;nimsuggestpowers IDE diagnostics. - Idiomatic patterns:
- Method-call syntax everywhere:
seq.map(f).filter(p).len. funcfor pure code; let the compiler verify.resultis implicitly defined as the named return;result.add xinstead of building a temp.- Use
enum+ variantobjectover class hierarchies. - Prefer templates over macros when pattern is simple.
- Method-call syntax everywhere:
- Expert review focus: GC mode mismatches (cross-mode FFI is dangerous),
refvs value object decisions, macro hygiene leaks, raised-exception specs ({.raises.}),cstring/stringboundary safety, ORC cycle handling for graph types,importpollution.
Ecosystem
- Web (backend): Jester (Sinatra-style), HappyX (full-stack), Prologue (Flask-like), Karax (frontend SPA via JS backend), Nim/Snip on top of asyncdispatch/chronos.
- Frontend: Karax (virtual DOM, compiles to JS), nigui (cross-platform GUI).
- Game / graphics: NimForUE (Unreal Engine bindings), naylib (raylib bindings), nico (PICO-8-like), godot-nim.
- Crypto / blockchain: Nimbus (Status’ Ethereum client) is the flagship Nim production project; large parts of
chronos,confutils,nim-ethcome from there. - Data / sci: Arraymancer (NumPy/PyTorch-like, including CUDA), Datamancer (DataFrames), measuremancer (units), bignum.
- Testing:
unittest(stdlib,suite/test),testament(the compiler’s own test runner). - Docs:
nim docgenerates HTML from##doc comments;nim doc2texfor LaTeX. Style: https://nim-lang.org/docs/lib.html. - Notable users: Status (Nimbus Ethereum client), ETH proof-of-stake validator infrastructure, several CLI utilities (e.g.,
choosenimitself), Nitter (Twitter frontend), Awesome Nim list.
Gotchas
- Style insensitivity:
myVarandmyvarandmy_varare the same identifier (after the first letter). This bites users of Python/Go conventions; tools complain but the compiler doesn’t. - GC mode is sticky: a library compiled assuming
--mm:refcwill surprise you under--mm:orc. Pin the mode in your.nimble’snimtask block or inconfig.nims. stringvscstring:stringis length-prefixed and managed;cstringis achar*. Conversion is mostly automatic but lifetimes are easy to mess up at FFI boundaries.charis one byte: not a rune. Iterationfor c in swalks bytes; usefor r in s.runesfromunicode.varparameters mutate in place, like Pascal’svar. Easy to confuse withvardeclarations.- Index from 0, ranges are inclusive on both ends with
..and exclusive-right with..<—0..3is[0,1,2,3],0..<3is[0,1,2]. - Implicit return
result: forgetting to assignresult(or leaving the function before doing so) returns the zero value silently. - Macro hygiene: macros generate symbols you might collide with; use
genSym()orquote docarefully. methodvsproc:methodis dynamic dispatch (vtable),procis static. Forgetting to usemethodon a polymorphic call gives the base-class behavior silently.--threads:onwas the old switch (now defaults on): pre-2.0 code might still use it explicitly.- Async cancellation in
asyncdispatchis poor; production code useschronosfor proper cancellation semantics. - Nimble lockfiles (
nimble.lock) are newer (2.x) — older projects don’t pin and can drift. nim cvsnim cppgenerates different ABIs; mixing in one project requires care.
Citations
- Nim Documentation index: https://nim-lang.org/documentation.html
- Nim Manual (canonical reference): https://nim-lang.org/docs/manual.html
- Nim Tutorial (Part I): https://nim-lang.org/docs/tut1.html
- Nim Tutorial (Part II): https://nim-lang.org/docs/tut2.html
- Nim Tutorial (Part III — macros): https://nim-lang.org/docs/tut3.html
- Nim by Example: https://nim-by-example.github.io/
- Memory management (ARC/ORC): https://nim-lang.org/docs/mm.html
- Macros module: https://nim-lang.org/docs/macros.html
- Backend integration (C/C++/JS/ObjC): https://nim-lang.org/docs/backends.html
- Nimble package manager: https://github.com/nim-lang/nimble
- choosenim: https://github.com/nim-lang/choosenim
- chronos (async): https://github.com/status-im/nim-chronos
- Status / Nimbus: https://nimbus.team/
- Wikipedia (history, MM modes, backends): https://en.wikipedia.org/wiki/Nim_(programming_language)