Getting started
Installation
Using choosenim (recommended)
curl https://nim-lang.org/choosenim/init.sh -sSf | sh
choosenim stable
Direct download
# Download from nim-lang.org
tar xvf nim-*.tar.xz
cd nim-*
sh build.sh
bin/nim c koch
./koch boot -d:release
./koch tools
Package manager
# macOS
brew install nim
# Ubuntu/Debian
apt install nim
# Arch Linux
pacman -S nim
Hello World
echo "Hello, World!"
Compile and run
nim c -r hello.nim # Compile and run
nim c hello.nim # Compile only
./hello # Run compiled binary
Quick Example
import strutils, sequtils
proc fibonacci(n: int): seq[int] =
result = @[0, 1]
for i in 2..<n:
result.add(result[^1] + result[^2])
let fibs = fibonacci(10)
echo fibs.map(proc(x: int): string = $x).join(", ")
# Output: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34
Basic Syntax
Variables
var x = 10 # Mutable variable
let y = 20 # Immutable variable
const PI = 3.14159 # Compile-time constant
var name: string # Type annotation
var age = 25 # Type inference
Multiple assignment
var
a = 1
b = "hello"
c = true
Comments
# Single-line comment
## Documentation comment
## for procedures and types
#[
Multi-line
comment
]#
discard """
Multi-line comment
alternative syntax
"""
Control Flow
if/elif/else
if x > 0:
echo "positive"
elif x < 0:
echo "negative"
else:
echo "zero"
case statement
case suit
of "hearts", "diamonds":
echo "red"
of "clubs", "spades":
echo "black"
else:
echo "unknown"
Loops
# For loop
for i in 0..<10:
echo i
# While loop
while x > 0:
dec x
# For-each
for item in items:
echo item
# With index
for idx, val in pairs(mySeq):
echo idx, ": ", val
Operators
# Arithmetic
+ - * / div mod
# Comparison
== != < > <= >=
# Logical
and or not xor
# Bitwise
and or not xor shl shr
# String concatenation
& (e.g., "Hello " & "World")
# Range
.. (inclusive) e.g., 1..10
..< (exclusive) e.g., 1..<10
Types
Basic Types
int, int8, int16, int32, int64
uint, uint8, uint16, uint32, uint64
float, float32, float64
bool
char
string
Examples
var
age: int = 25
price: float = 19.99
initial: char = 'A'
name: string = "Alice"
isActive: bool = true
Custom Types
Type aliases
type
PersonId = int
Name = string
Object types
type
Person = object
name: string
age: int
var p = Person(name: "Bob", age: 30)
echo p.name # Bob
Enum types
type
Color = enum
red, green, blue
Direction = enum
north = "N"
south = "S"
east = "E"
west = "W"
var color = red
var dir = north
Tuple types
type
Point = tuple[x, y: int]
var p: Point = (10, 20)
echo p.x, ", ", p.y
# Anonymous tuple
var coord = (x: 5, y: 15)
Distinct Types
type
Meters = distinct float
Kilograms = distinct float
var distance = 100.0.Meters
# distance + 50.0 # Error: type mismatch
# Requires explicit conversion
distance = (distance.float + 50.0).Meters
Range Types
type
Natural = range[0..high(int)]
Percentage = range[0..100]
var score: Percentage = 85
# score = 150 # Error: out of range
Collections
Sequences
Dynamic arrays, reference type.
var numbers: seq[int] = @[1, 2, 3]
numbers.add(4)
numbers.delete(0)
echo numbers[0] # 2
echo numbers[^1] # Last element: 4
echo numbers[0..1] # Slice: @[2, 3]
Sequence operations
import sequtils
var nums = @[1, 2, 3, 4, 5]
# Map
let doubled = nums.map(proc(x: int): int = x * 2)
# Filter
let evens = nums.filter(proc(x: int): bool = x mod 2 == 0)
# Fold
let sum = nums.foldl(a + b)
Arrays
Fixed-size, stack-allocated.
var arr: array[5, int] = [1, 2, 3, 4, 5]
var matrix: array[0..2, array[0..2, int]]
echo arr[0] # 1
echo arr.len # 5
Array initialization
var zeros: array[10, int] # All zeros
var filled = [1, 1, 1, 1, 1]
Sets
Unordered collection of ordinal types.
var fruits = {"apple", "banana", "cherry"}
fruits.incl("date")
fruits.excl("banana")
echo "apple" in fruits # true
# Set operations
let a = {1, 2, 3}
let b = {2, 3, 4}
echo a + b # Union: {1, 2, 3, 4}
echo a * b # Intersection: {2, 3}
echo a - b # Difference: {1}
Tables
Hash maps/dictionaries.
import tables
var ages = initTable[string, int]()
ages["Alice"] = 30
ages["Bob"] = 25
# With literal syntax
var scores = {"Alice": 95, "Bob": 87}.toTable
echo ages["Alice"] # 30
echo ages.getOrDefault("Eve", 0) # 0
# Iteration
for name, age in ages:
echo name, " is ", age
OrderedTable
import tables
var orderedScores = initOrderedTable[string, int]()
# Maintains insertion order
Procedures
Basic Procedures
proc greet(name: string): string =
result = "Hello, " & name
proc add(a, b: int): int =
return a + b
# Without return type
proc printSum(a, b: int) =
echo a + b
echo greet("World") # Hello, World
echo add(5, 3) # 8
Parameters
Default parameters
proc greet(name: string = "Guest"): string =
"Hello, " & name
echo greet() # Hello, Guest
echo greet("Alice") # Hello, Alice
var parameters
proc increment(x: var int) =
inc x
var num = 5
increment(num)
echo num # 6
Multiple return values
proc divmod(a, b: int): tuple[quotient, remainder: int] =
result = (a div b, a mod b)
let (q, r) = divmod(17, 5)
echo q, " remainder ", r # 3 remainder 2
Method Call Syntax
proc double(x: int): int = x * 2
echo 5.double() # 10
echo double(5) # 10 (equivalent)
# Chaining
echo "hello".toUpper().replace("L", "X")
Overloading
proc add(a, b: int): int = a + b
proc add(a, b: float): float = a + b
proc add(a, b: string): string = a & b
echo add(1, 2) # 3
echo add(1.5, 2.5) # 4.0
echo add("Hi", "!") # Hi!
Generic Procedures
proc identity[T](x: T): T =
return x
echo identity(42) # 42
echo identity("text") # text
proc max[T](a, b: T): T =
if a > b: a else: b
echo max(5, 10) # 10
echo max(3.14, 2.71) # 3.14
Memory Management
Reference Types
type
Node = ref object
value: int
next: Node
var head = Node(value: 1)
head.next = Node(value: 2)
# Automatic garbage collection
Pointer Types
var x = 10
var p: ptr int = addr x
echo p[] # 10 (dereference)
p[] = 20
echo x # 20
Unsafe operations
# Requires --mm:none or explicit permission
var raw = cast[ptr int](alloc0(sizeof(int)))
raw[] = 42
dealloc(raw)
Memory Management Options
# Compile with different GC/MM
nim c --mm:refc myfile.nim # Deferred RC (default)
nim c --mm:arc myfile.nim # ARC (Automatic Reference Counting)
nim c --mm:orc myfile.nim # ORC (ARC + cycle collector)
nim c --mm:none myfile.nim # No memory management
ARC/ORC benefits
- Deterministic destruction
- No GC pauses
- Better for real-time systems
- Ref counting overhead
Manual Memory
proc allocBuffer(size: int): ptr UncheckedArray[byte] =
result = cast[ptr UncheckedArray[byte]](alloc(size))
proc freeBuffer(buffer: ptr UncheckedArray[byte]) =
dealloc(buffer)
var buf = allocBuffer(1024)
# Use buffer
freeBuffer(buf)
Async & Concurrency
Async/Await
import asyncdispatch, httpclient
proc fetchUrl(url: string): Future[string] {.async.} =
var client = newAsyncHttpClient()
result = await client.getContent(url)
proc main() {.async.} =
let content = await fetchUrl("https://example.com")
echo content.len
waitFor main()
Multiple async calls
import asyncdispatch
proc task1(): Future[int] {.async.} =
await sleepAsync(100)
return 1
proc task2(): Future[int] {.async.} =
await sleepAsync(200)
return 2
proc main() {.async.} =
let results = await all(task1(), task2())
echo results # @[1, 2]
waitFor main()
Threads
import std/[threadpool, locks]
var lock: Lock
initLock(lock)
proc worker(id: int) {.thread.} =
withLock lock:
echo "Worker ", id, " running"
spawn worker(1)
spawn worker(2)
sync() # Wait for all threads
Thread pool
import threadpool
proc compute(x: int): int = x * x
var futures: seq[FlowVar[int]]
for i in 1..10:
futures.add(spawn compute(i))
for fut in futures:
echo ^fut # Get result
Channels
import std/[channels, threadpool]
var chan: Channel[int]
chan.open()
proc sender() {.thread.} =
for i in 1..5:
chan.send(i)
proc receiver() {.thread.} =
while true:
let msg = chan.recv()
if msg == 5: break
echo "Received: ", msg
spawn sender()
spawn receiver()
sync()
chan.close()
Modules & Packages
Importing Modules
import strutils # Import all
import strutils, sequtils # Multiple imports
from os import sleep, `/` # Selective import
import strutils except split # Exclude symbols
Qualified imports
import std/tables as t
var myTable = t.initTable[string, int]()
Include
include mymodule # Textual inclusion
Creating Modules
# mymath.nim
proc add*(a, b: int): int = # * exports symbol
a + b
proc internal(x: int): int = # Not exported
x * 2
const Version* = "1.0.0"
Usage
import mymath
echo add(5, 3) # Works
# echo internal(5) # Error: undeclared identifier
echo Version # 1.0.0
Nimble Package Manager
# Install package
nimble install package_name
# Search packages
nimble search json
# Initialize project
nimble init
# Install dependencies
nimble install
# Build project
nimble build
nimble file
# Package
version = "0.1.0"
author = "Your Name"
description = "My package"
license = "MIT"
# Dependencies
requires "nim >= 2.0.0"
requires "regex >= 0.20.0"
Standard Library
import std/[
strutils, # String utilities
sequtils, # Sequence utilities
tables, # Hash tables
sets, # Set operations
json, # JSON parsing
httpclient, # HTTP client
os, # OS operations
times, # Date/time
math, # Mathematical functions
asyncdispatch # Async support
]
Compilation
Compile Commands
nim c file.nim # Compile to C
nim c -r file.nim # Compile and run
nim c -d:release file.nim # Release build
nim cpp file.nim # Compile to C++
nim js file.nim # Compile to JavaScript
Optimization flags
nim c -d:release --opt:speed file.nim
nim c -d:release --opt:size file.nim
nim c -d:danger file.nim # No runtime checks
Backend Targets
C backend (default)
nim c myfile.nim
# Outputs: myfile (executable)
C++ backend
nim cpp myfile.nim
# Uses C++ compiler
JavaScript backend
nim js -d:nodejs myfile.nim # Node.js target
nim js -d:browser myfile.nim # Browser target
Cross-Compilation
# Windows from Linux
nim c --os:windows --cpu:amd64 file.nim
# Linux ARM
nim c --os:linux --cpu:arm64 file.nim
# macOS
nim c --os:macosx --cpu:amd64 file.nim
Common targets
--cpu:i386, amd64, arm, arm64
--os:linux, windows, macosx, freebsd
Compile-time Options
# Define constants
nim c -d:ssl file.nim
# In code
when defined(ssl):
import openssl
else:
echo "SSL not enabled"
# Version checks
when NimMajor >= 2:
# Nim 2.0+ specific code
Configuration files
# config.nims
switch("mm", "orc")
switch("opt", "speed")
switch("define", "release")
Templates & Macros
Templates
Code generation at compile time.
template withFile(filename: string, body: untyped) =
var f = open(filename)
try:
body
finally:
f.close()
withFile("test.txt"):
echo f.readLine()
Generic templates
template max(a, b: untyped): untyped =
if a > b: a else: b
echo max(10, 20) # 20
echo max("abc", "xyz") # xyz
Debug template
template debug(msg: string) =
when defined(debug):
echo "[DEBUG] ", msg
debug("Starting process") # Only in debug builds
Macros
AST manipulation at compile time.
import macros
macro myAssert(condition: untyped): untyped =
result = quote do:
if not `condition`:
raise newException(AssertionDefect,
"Assertion failed: " & astToStr(`condition`))
myAssert(2 + 2 == 4)
Dump macro
import macros
dumpTree:
if x > 0:
echo "positive"
# Prints AST structure
Gotchas & Best Practices
Partial Case Insensitivity
Nim is style-insensitive but not fully case-insensitive.
proc myFunction() = discard
myFunction() # Works
myfunction() # Works
my_function() # Works
MyFunction() # Works
# MYFUNCTION() # Error (first char matters)
Convention: Use camelCase for consistency.
Indentation is Strict
# Correct
if x > 0:
echo "positive"
echo "done"
# Error: inconsistent indentation
if x > 0:
echo "positive"
echo "done" # Extra space
Use 2 spaces (standard) or 4 spaces consistently.
var Parameters in Async
# Error: var parameters not allowed in async
proc asyncProc(x: var int) {.async.} =
discard
# Solution: Use ref types
proc asyncProc(x: ref int) {.async.} =
x[] += 1
Result Variable
Implicit return variable in functions.
proc sum(a, b: int): int =
result = a + b # Implicitly returned
proc greet(name: string): string =
result = "Hello"
result.add(", ")
result.add(name)
String Mutability
var s = "hello"
s[0] = 'H' # Works
echo s # Hello
# String concatenation creates new string
var a = "foo"
var b = a
a.add("bar")
echo b # foo (unchanged)
Method vs Proc
# Proc: static dispatch
proc greet(x: int) = echo "int"
proc greet(x: string) = echo "string"
# Method: dynamic dispatch
method speak(a: Animal) {.base.} = echo "..."
method speak(d: Dog) = echo "Woof"
Methods are slower (virtual dispatch).
Memory Safety
# Seq vs Array
var s = @[1, 2, 3] # Heap allocated
var a = [1, 2, 3] # Stack allocated
# Prefer seq for dynamic data
# Prefer array for fixed-size data
Integer Division
echo 5 / 2 # 2.5 (float division)
echo 5 div 2 # 2 (integer division)
echo 5 mod 2 # 1 (modulo)
Also see
- Nim Official Website - Main documentation and downloads
- Nim Tutorial - Official beginner tutorial
- Nim Standard Library - Complete stdlib reference
- Nim by Example - Practical code examples
- Nimble Package Directory - Search Nim packages
- Nim Forum - Community discussion and support
- Nim Manual - Complete language specification