Overview
Nim is a statically-typed compiled systems programming language that combines the performance of C with the expressiveness of Python. It features a clean, indentation-based syntax, a powerful macro system for metaprogramming, and compiles to C, C++, JavaScript, or Objective-C. Nim’s memory management options range from a traditional garbage collector to deterministic destructors (ARC/ORC), giving developers fine-grained control over performance characteristics.
Created by Andreas Rumpf in 2008, Nim has evolved into a versatile language suitable for systems programming, web development, scripting, and game development. Its compile-time code execution, generic programming, and template system enable zero-cost abstractions. Nim’s standard library is comprehensive, and its package manager Nimble provides access to thousands of community packages. The language prioritizes efficiency, expressiveness, and elegance.
Installation
Package Managers
# macOS
brew install nim
# Ubuntu/Debian
apt install nim
# Windows (scoop)
scoop install nim
# Using choosenim (version manager, recommended)
curl https://nim-lang.org/choosenim/init.sh -sSf | sh
choosenim stable
Verify Installation
nim --version
nimble --version
# Compile and run
nim c -r hello.nim # Compile C backend + run
nim cpp -r hello.nim # Compile C++ backend + run
nim js hello.nim # Compile to JavaScript
Core Language
Variables and Types
# Immutable (let) and mutable (var)
let name = "Nim" # Inferred as string
let pi: float = 3.14159
var counter = 0
counter += 1
# Constants (compile-time)
const MaxSize = 1024
const Version = "1.0.0"
# Basic types
let i: int = 42
let f: float64 = 3.14
let b: bool = true
let c: char = 'A'
let s: string = "hello"
Primitive Types
| Type | Description | Example |
|---|
int | Platform-sized integer | 42 |
int8 to int64 | Sized signed integers | 42'i32 |
uint8 to uint64 | Sized unsigned integers | 255'u8 |
float32, float64 | Floating point | 3.14 |
bool | Boolean | true, false |
char | Single character | 'A' |
string | Mutable string | "hello" |
cstring | C-compatible string | cstring"hello" |
seq[T] | Dynamic array | @[1, 2, 3] |
array[N, T] | Fixed-size array | [1, 2, 3] |
Control Flow
# If/elif/else
if x > 0:
echo "positive"
elif x == 0:
echo "zero"
else:
echo "negative"
# Case statement (exhaustive for enums)
case command
of "start": startServer()
of "stop": stopServer()
of "status": showStatus()
else: echo "Unknown command"
# When (compile-time if)
when defined(windows):
echo "Windows"
elif defined(linux):
echo "Linux"
else:
echo "Other OS"
# For loops
for i in 0..9:
echo i
for i in countup(0, 10, 2): # Step by 2
echo i
for item in items:
echo item
for i, item in items: # With index
echo i, ": ", item
# While loop
var n = 10
while n > 0:
echo n
dec n
Procedures and Functions
# Procedure (may have side effects)
proc greet(name: string): string =
result = "Hello, " & name & "!"
# Function (no side effects)
func add(a, b: int): int =
a + b
# Default parameters
proc connect(host: string, port: int = 8080, ssl: bool = false) =
echo "Connecting to ", host, ":", port
# Varargs
proc sum(args: varargs[int]): int =
for x in args:
result += x
echo sum(1, 2, 3, 4) # 10
# Overloading
proc display(x: int) = echo "Int: ", x
proc display(x: string) = echo "Str: ", x
# Method call syntax (UFCS)
"hello".len # Same as len("hello")
@[3,1,2].sorted # Same as sorted(@[3,1,2])
# Result variable (implicit return)
proc factorial(n: int): int =
result = 1
for i in 2..n:
result *= i
Collections
# Sequences (dynamic arrays)
var nums = @[1, 2, 3]
nums.add(4)
nums.delete(0)
echo nums[^1] # Last element
echo nums[1..2] # Slice
# Arrays (fixed size)
var arr: array[5, int] = [1, 2, 3, 4, 5]
# Tables (hash maps)
import std/tables
var users = initTable[string, int]()
users["alice"] = 30
users["bob"] = 25
if users.hasKey("alice"):
echo users["alice"]
for key, value in users:
echo key, ": ", value
# Sets
import std/sets
var s = initHashSet[int]()
s.incl(1)
s.incl(2)
echo 1 in s # true
# Tuples
let point = (x: 10, y: 20)
echo point.x
let (a, b) = (1, "hello")
Object-Oriented Features
# Object types
type
Animal = ref object of RootObj
name: string
age: int
Dog = ref object of Animal
breed: string
Cat = ref object of Animal
indoor: bool
# Methods
method speak(a: Animal): string {.base.} =
"..."
method speak(d: Dog): string =
"Woof!"
method speak(c: Cat): string =
"Meow!"
# Enum types
type
Color = enum
Red, Green, Blue
Direction = enum
North = "N"
South = "S"
East = "E"
West = "W"
# Distinct types (newtypes)
type
Dollars = distinct float
Euros = distinct float
Error Handling
# Exceptions
try:
let data = readFile("config.txt")
echo data
except IOError as e:
echo "IO Error: ", e.msg
except:
echo "Unknown error"
finally:
echo "Cleanup"
# Raising exceptions
proc divide(a, b: float): float =
if b == 0:
raise newException(DivByZeroDefect, "Division by zero")
a / b
# Option type
import std/options
proc findUser(name: string): Option[string] =
if name == "admin":
some("Admin User")
else:
none(string)
let user = findUser("admin")
if user.isSome:
echo user.get()
Build and Package Management
Nimble (Package Manager)
# Create new project
nimble init myproject
# Install package
nimble install jester
nimble install karax
# Build project
nimble build
# Run project
nimble run
# List installed packages
nimble list --installed
# Search packages
nimble search http
Compilation Options
| Command | Description |
|---|
nim c -r main.nim | Compile (C) and run |
nim c -d:release main.nim | Release build (optimized) |
nim c -d:danger main.nim | Maximum optimization (no checks) |
nim c --mm:orc main.nim | Use ORC memory management |
nim c --mm:arc main.nim | Use ARC memory management |
nim c --threads:on main.nim | Enable threading |
nim c --opt:speed main.nim | Optimize for speed |
nim c --opt:size main.nim | Optimize for size |
nim js main.nim | Compile to JavaScript |
Configuration
nim.cfg / config.nims
# config.nims
switch("mm", "orc")
switch("threads", "on")
switch("d", "ssl")
when defined(release):
switch("opt", "speed")
switch("passC", "-flto")
# Task definitions
task build, "Build the project":
exec "nim c -d:release src/main.nim"
task test, "Run tests":
exec "nim c -r tests/test_all.nim"
Advanced Usage
Templates and Macros
# Templates (inline code substitution)
template `!=`(a, b: untyped): bool =
not (a == b)
template withFile(f, filename, mode, body: untyped) =
var f: File
if open(f, filename, mode):
try:
body
finally:
close(f)
else:
raise newException(IOError, "Cannot open: " & filename)
withFile(file, "data.txt", fmRead):
echo file.readAll()
# Macros (AST transformation)
import std/macros
macro dumpTree(body: untyped): untyped =
echo body.treeRepr
body
macro createEnum(name: untyped, fields: varargs[untyped]): untyped =
result = nnkTypeSection.newTree(
nnkTypeDef.newTree(name, newEmptyNode(),
nnkEnumTy.newTree(newEmptyNode())))
for field in fields:
result[0][2].add(field)
Async/Await
import std/asyncdispatch
proc fetchData(url: string): Future[string] {.async.} =
let client = newAsyncHttpClient()
let response = await client.getContent(url)
return response
proc main() {.async.} =
let data = await fetchData("https://api.example.com/data")
echo data
waitFor main()
FFI (C Interop)
# Import C functions
proc printf(format: cstring): cint {.importc, varargs, header: "<stdio.h>".}
# Wrap C library
{.passL: "-lsqlite3".}
type Sqlite3 = distinct pointer
proc sqlite3_open(filename: cstring, db: ptr Sqlite3): cint
{.importc, header: "<sqlite3.h>".}
Troubleshooting
| Problem | Solution |
|---|
undeclared identifier | Check import statements; Nim is case-insensitive but underscore-insensitive |
type mismatch | Use explicit type conversion; check proc signatures |
| GC pauses | Switch to ARC/ORC with --mm:orc |
| Slow compilation | Use --incremental:on; split into modules |
| C codegen issues | Check generated C in nimcache/; use {.emit.} pragma |
| JS backend differences | Some stdlib modules not available; check docs |
| Memory leaks | Use --mm:orc for cycle collection; check ref objects |
SIGSEGV | Check nil dereferences; use --panics:on for better traces |