NexusCS

Nim

Systems programming
Quick reference for Nim - a statically typed compiled systems programming language with Python-like syntax, metaprogramming capabilities, and multiple backend targets (C/C++/JS).
nim
systems
compiled
metaprogramming

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