Logo

0x3d.site

is designed for aggregating information and curating knowledge.

Nim

The Nim quick reference cheat sheet that aims at providing help on writing basic syntax and methods.
Category: Programming


Getting Started

helloworld.nim {.row-span-2}

echo "Hello World!"

# This is a comment
echo "What's your name? "
var name: string = readLine(stdin)
# readLine(stdin) asks you to type in a string.

echo "Hi, ", name, "!"

Compile helloworld.nim with nim compile

$ nim compile helloworld.nim

Run the compiled binary helloworld (or helloworld.exe if you're on Windows)

./helloworld
Hello, World!
What's your name?
Hi, <name>!

There is also a possibility to both compile and run the program with just one command. We need to type: $ nim c -r helloworld.nim

Common compile commands (terminal)

nim c -r main.nim          # debug build, run after compile
nim c -d:release main.nim  # optimized release build
nim c --mm:arc main.nim    # ARC memory management (Nim 2 default)

Useful Resources {.col-span-2}

Variables {.row-span-1}

# 'var' declares mutable variables.

var number: int = 100 # Single line declaration

# A 'var' block can declare multiple variables at once.

var
  letter: char = 'n'
  lang = "N" & "im"
  length = len(lang)
  counter: int = 7
  boat: float
  truth = true

Immutable Variables (let)

# 'let' declares immutable variables.
let
  salute: string = "Hello!"
  x: int = 10
  y = x + 5

let z = 20
z = 500   # Error: 'z' is immutable

Comments

# This is a comment

#[
  This is a multiline comment.
  In Nim, multiline comments can be nested, beginning with #[
  ... and ending with ]#
]#

Constants {.row-span-1}

const
  debug = true
  compileBadCode = false

# Constants are declared at compile time and cannot be changed.

"For" Loops

for item in (1 ..< 6):
  if item == 3: break
  echo item

echo "All done!"

See: Loops

If Statements

import std/strutils
let number = parseInt(readLine(stdin))

if number <= 5:
  echo "Small sized number."
elif number > 5 and number <= 25:
  echo "Medium sized number."
else:
  echo "Big number."

See: Flow Control

Procedures / Functions

proc myFunction() =
  echo "Hello, this is my function"

myFunction()

# prints "Hello, this is my function"

See: Procedures

Formatted Strings

import std/strformat

let
  a = "Hello there."
  b = "How's it going?"
  c = "These are variables."

echo fmt"{a} {b} {c}" # "Hello there. How's it going? These are variables."

See: Imports

Data Types

Integers

let
  a = 11
  b = 4

echo "a + b = ", a + b # 15
echo "a - b = ", a - b # 7
echo "a * b = ", a * b # 44
echo "a / b = ", a / b # 2.75
echo "a div b = ", a div b # 2
echo "a mod b = ", a mod b # 3

Floats

let
  c = 6.75
  d = 2.25

echo "c + d = ", c + d # 9.0
echo "c - d = ", c - d # 4.5
echo "c * d = ", c * d # 15.1875
echo "c / d = ", c / d # 3.0

Strings {.row-span-3}

let
  m = "word"
  n = "A sentence with punctuation."
  o = ""
  p = "32"
  q = "!"

echo m
# prints "word"
echo p, " is a string."
# prints "32 is a string."

String Concatenation

var
  p = "abc"
  q = "xy"
  r = 'z'

p.add("def")   # modifies p
q.add(r)       # chars can be added to strings

echo p         # "abcdef"
echo q         # "xyz"

# `&` creates a new string:
echo p & q     # "abcdefxyz"

Special Escape Sequences

echo "line1\nline2"     # newline
echo "col1\tcol2"       # tab
echo "A\\B\\C"          # backslash

# Raw strings ignore escapes:
echo r"line1\nline2"

See also: Escape Sequences

Characters

let
  h = 'z'
  i = '+'
  j = '2'
  k = '35'   # error: too many characters
  l = 'xy'   # error: too many characters

Booleans

let x = true
let y = false

# Boolean literals are lowercase.

Type Conversion {.col-span-2}


let e = 5
let f = 23.987

# Converting between ints and floats:
echo float(e)    # 5.0
echo int(f)      # 23

# Mixed operations require explicit conversion:
echo float(e) + f    # 28.987
echo e + int(f)      # 28

Compound Types

Arrays

type IntArray = array[0..3, int]

let a: IntArray = [10, 20, 30, 40]

echo a[0]      # 10
echo a.len     # 4

Sequences

var s: seq[int] = @[1, 2, 3]
s.add(4)

echo s         # @[1, 2, 3, 4]
echo s.len     # 4

Objects

type Person = object
  name: string
  age: int

let p = Person(name: "Ana", age: 20)
echo p.name

Tuples

var person = (name: "Sophie", age: 18)

echo person.name # Sophie
echo person.age  # 18

# swapping values:
var (x, y) = (1, 2)
(x, y) = (y, x)
echo x, " ", y     # 2 1

Sets

type CharSet = set[char]

let letters: CharSet = {'a'..'z'}

echo 'a' in letters    # true
echo 'Z' in letters    # false

Reference Objects

type Node = ref object
  value: int
  next: Node     # linked list style

var n = Node(value: 10)
echo n.value

Needed for Object Oriented Programming within Nim.

Slicing Sequences and Strings {.col-span-2}

let xs = @[10, 20, 30, 40]

echo xs[1..2]    # @[20, 30]
echo xs[^2..^1]  # @[30, 40]

let s = "abcdef"

echo s[0..2]     # "abc"
echo s[3..^1]    # "def" (^1 = last char)
echo s[1..<4]    # "bcd"

Tables/Dictionaries

import std/tables

var ages = initTable[string, int]()
ages["Ana"] = 20
ages["Sophie"] = 18

echo ages["Ana"]                 # 20
echo ages.getOrDefault("X", -1)  # -1

for name, age in ages:
  echo name, ": ", age

Operators

Arithmetic Operators {.row-span-1}

var
  a = 10
  b = 3

echo a + b      # 13
echo a - b      # 7
echo a * b      # 30
echo a / b      # 3.333333
echo a div b    # 3
echo a mod b    # 1
echo a ^ b      # 1000

Operator Name Example
+ Add x + y
- Subtract x - y
* Multiply x * y
/ Float Divide x / y
div Integer Divide x div y
mod Modulo x mod y
^ Power x ^ y

{.show-header}

Assignment

These only work on mutable (var) variables, not on let or const.

var value = 1990
echo value # 1990

value = 2
echo value # 2

Operator Description Example
= Assign value x = y

{.show-header}

Update operations

Syntax Expands to Meaning
x += y x = x + y add, assign
x -= y x = x - y subtract, assign
x *= y x = x * y multiply, assign

{.show-header}

Other variants like /= or mod= are not available,
so just write them out explicitly, e.g. x = x / y.

Comparison Operators {.row-span-2}

let
  x = 5
  y = 3

echo x > y
# prints "true" because 5 is greater than 3

Symbol Meaning Example
== equals if x == y...
!= not equal to if x != y...
> greater than if x > y...
< less than if x < y...
>= greater than or equal to if x >= y...
<= less than or equal to if x <= y...

{.show-header}

Comparison operators are used to compare two values.

Lexicographical Comparison

They also work on char and string values via lexicographic order.

echo "apple" < "banana"  # true
echo "axyz" < "axyb"     # true
echo "ooooo" > "o"       # true
echo "abc" < "ab"        # false

echo 'a' < 'd'           # true
echo 'a' < 'Z'           # false

Logical Operators {.row-span-2} {.col-span-2}

Operator Example Meaning
and if a > 0 and a < 10: From 1 to 9
or if x == 0 or y == 0: Either one of them is 0
not if not isValid: If isValid is not true
xor if flagA xor flagB: Only one of them is true

{.show-header}

Logical operators work on bool.

Extra Bitwise Operators

Operator Example Result (with a = 60, b = 13)
and a and b 12
or a or b 61
xor a xor b 49
not not a -61
shl a shl 2 240
shr a shr 2 15

{.show-header}

These work on integers bit-by-bit; use them for flags, masks, low-level code, etc.

Flow Control

Match Case {.row-span-3}

let x = 5
case x:
of 0, 1:
  echo "small"
of 2, 3, 4:
  echo "medium"
else:
  echo "large"

case of can look for ranges too.

let age = 17

case age:
of 0..12:
  echo "child"
of 13..19:
  echo "teen"
else:
  echo "adult"

...and strings and chars, too

let cmd = "quit"

case cmd:
of "help":
  echo "show help"
of "quit", "exit":
  echo "bye!"
else:
  echo "unknown command"

let ch = 'g'
case ch:
of {'a', 'e', 'i', 'o', 'u'}:
  echo "vowel"
else:
  echo "consonant or symbol"

Basic If-Else {.col-span-2}

let num = 5

if num > 10:
  echo "num is bigger than 10"
elif num < 10:
  echo "num is smaller than 10"
else:
  echo "num is exactly 10"

One Liners {.col-span-2}

let
  a = 330
  b = 200

let r = if a > b: a else: b

echo r # 330

Compile-Time Conditions {.col-span-2}

when defined(linux):
  echo "Compiled on Linux"
elif defined(windows):
  echo "Compiled on Windows"
else:
  echo "Unknown OS"

Loops

For Loops {.row-span-2}

for i in 1..5:
  echo i
# 1 2 3 4 5

for i in 0..<3:
  echo i
# 0 1 2

# i = index, v = value in sequence
for i, v in @[10, 20, 30]:
  echo i, ": ", v
# 0: 10
# 1: 20
# 2: 30

Keep in mind:
1..5 = Inclusive (1 2 3 4 5)
1..<5 = Exclusive (1 2 3 4)

While Loops

var counter = 0

while counter <= 10:
  counter = counter + 1
  echo counter # prints 1..10

Infinite Loops

while true:
  echo "ay! "

while 1 > 0:
  echo "echo... "

Break Statements

var query = ""
while true:
  echo "Do you want out?"
  query = readLine(stdin)
  if query == "yes":
    echo "Well then."
    break
  else:
    echo "I'll ask again."

Continue Statements

for i in 1..5:
  if i mod 2 == 0: continue
  echo i
# prints only odd numbers: 1 3 5

Procedures

Simple Procedure

proc salute() =
  echo "Hello!"

salute() # prints "Hello!"

Procedures with Parameters

proc addition(a: int, b: int): int =
  a + b

echo addition(20, 30) # returns 50

Using result {.row-span-2}

proc double(x: int): int =
  result = x * 2

echo double(5) # returns 10

Useful when building results step-by-step:

proc sumAll(xs: seq[int]): int =
  for n in xs:
    result += n

echo sumAll(@[1,1,1,1,1]) # returns 5

Default Parameters {.col-span-2}

proc welcome(name = "stranger") =
  echo "Hi, ", name

welcome("Sophie") # prints "Hi, Sophie"
welcome()         # uses default, prints "Hi, stranger"

Procedures returning multiple values

proc divmod(a, b: int): (int, int) =
  (a div b, a mod b)

let (q, r) = divmod(10, 3)
echo q, " ", r   # 3 1

Anonymous Procedures (Lambdas)

let square = proc(x: int): int = x * x
echo square(5) # 25

Procedures within Procedures {.row-span-2}

proc outer() =
  var count = 0

  proc inner() =
    inc(count)
    echo count

  inner()
  inner()

outer()   # prints 1 then 2

Overloading

proc show(x: int) = echo "int: ", x
proc show(x: string) = echo "string: ", x

show(10)
show("hi")

Discardable Procedure Returns

proc doSomething(): int {.discardable.} =
  123

doSomething()      # allowed now

Object-Oriented Programming

Ref Objects and Inheritance {.row-span-2}

type
  Animal = ref object of RootObj
    name: string

  Dog = ref object of Animal
    breed: string

method speak(a: Animal) {.base.} =
  echo a.name, " makes a sound."

method speak(d: Dog) =
  echo d.name, " barks!"

var
  a: Animal = Animal(name: "Creature")
  d: Animal = Dog(name: "Fido", breed: "Mutt")  # Dog upcasted to Animal

speak(a)  # Creature makes a sound.
speak(d)  # Fido barks!

Use ref object of RootObj + method when you need runtime polymorphism.

Procs as Methods

type
  Counter = ref object
    value: int

proc inc(c: var Counter) =
  inc c.value

var c = Counter(value: 0)
inc(c)
echo c.value  # 1

Constructor Procs

type
  Dog = ref object
    name: string
    age: int

proc newDog(name: string; age: int): Dog =
  Dog(name: name, age: age)

let d = newDog("Fido", 3)
echo d.name, " is ", d.age, " years old"

Type tests and Downcasting {.col-span-2}

type
  Animal = ref object of RootObj
    name: string

  Dog = ref object of Animal
    breed: string

proc newDog(name, breed: string): Dog =
  Dog(name: name, breed: breed)

var a: Animal = newDog("Fido", "mutt")

if a of Dog:                       # runtime type check
  let d = Dog(a)                   # downcast
  echo d.name, " is a ", d.breed

x of Type checks the dynamic type; Type(x) downcasts (unsafe if you lie).

Imports and Modules

Basic Imports

import std/math

echo sqrt(9.0) # 3.0

See also: Standard Library

Importing Multiple Modules

import std/[strutils, sequtils]

echo "hi".toUpperAscii()    # "HI"
echo @[1,2,3].mapIt(it * 2) # @[2, 4, 6]

Importing Specific Modules

from std/strutils import toLowerAscii

echo toLowerAscii("ABC")  # "abc"

Renaming Modules

import std/random as rng

rng.randomize()

echo rng.rand(1..10) # anywhere from 1 to 10

Don't forget to use randomize() if you're using the std/random library, or else you won't have randomness.

Creating Modules {.col-span-2}

# utils.nim
proc greet*(name: string) =
  echo "Hello, ", name

(The asterisk means "export this procedure from the module".)


# main.nim
import utils

greet("John") # prints "Hello, John"

when isMainModule

proc run() =
  echo "Running..."

when isMainModule:
  run()  # Only runs when this file is the program entry point

Code inside when isMainModule: is skipped when the file is imported.

Re-exporting {.col-span-2}

# utils.nim
import std/strutils
export strutils    # re-export everything from strutils

Now main.nim can use toUpperAscii directly, through utils.

# main.nim
import utils

echo "hi".toUpperAscii()

Prelude Module {.col-span-3}

import std/prelude
include std/prelude
  # same as:
  # import std/[os, strutils, times, parseutils, hashes, tables, sets, sequtils, parseopt, strformat]
let x = 1
assert "foo $# $#" % [$x, "bar"] == "foo 1 bar"
assert toSeq(1..3) == @[1, 2, 3]
when not defined(js) or defined(nodejs):
  assert getCurrentDir().len > 0
  assert ($now()).startsWith "20"

Error Handling

Try - Except - Finally {.row-span-2}

import std/strutils

try:
  let x = parseInt("42a")         # invalid integer
  echo x
except ValueError as e:
  echo "Value error: ", e.msg     # catch conversion error
except IOError:
  echo "I/O error!"
finally:
  echo "This always runs"

Raising Exceptions

type
  MyError* = object of Exception

proc loadData(name: string) =
  if name == "":
    raise newException(MyError, "No name provided")
  # ... else proceed

loadData("")  # raises MyError

defer for Cleanup

proc processFile(path: string) =
  var f = open(path, fmRead)
  defer:
    close(f)
  # work with f …

Try as Exception

import std/strutils

let result = try: parseInt("bad")
             except ValueError: -1

echo result  # prints -1

Exception Tracking with raises

proc riskyOperation() {.raises: [IOError].} =
  # this proc may raise IOError
  raise newException(IOError, "fail")

# A proc with {.raises: []} must not raise any catchable exception.

Enums

Basic Usage

type
  Direction = enum
    north, east, south, west

var d = north

if d == north:
  echo "Going up!"

Enums and case

# with the same Direction enum:

var d = north

case d:
  of north: echo "N"
  of south: echo "S"
  of east:  echo "E"
  of west:  echo "W"

Enum as an Ordinal

# with the same Direction enum:
echo ord(north)   # 0
echo succ(north)  # east
echo pred(south)  # east

Iterators

Basic Iterator {.row-span-2}

iterator countUp(a, b: int): int =
  var i = a
  while i <= b:
    yield i      # `yield` produces a value for the `for` loop
    inc i

for n in countUp(1, 5):
  echo n
# 1 2 3 4 5

Common Built-In Iterators

for i in countup(1, 3):
  echo i               # 1 2 3

for i in countdown(3, 1):
  echo i               # 3 2 1

Defining your own Iterator {.row-span-2}

iterator evensUpTo(n: int): int =
  var x = 0
  while x <= n:
    yield x
    x += 2

for e in evensUpTo(10):
  echo e        # 0 2 4 6 8 10

Files & IO

Check Paths and Existence {.row-span-2}

import std/os

if fileExists("data.txt"):
  echo "File exists!"

if dirExists("configs"):
  echo "Directory exists!"

echo getCurrentDir()
echo absolutePath("data.txt")

Read / Write Whole File {.row-span-1}

# Write whole file
writeFile("out.txt", "Hello Nim!\n")

# Read whole file
let text = readFile("out.txt")
echo text

Manual Open/Close with defer {.row-span-2}

import std/syncio

proc writeLog() =
  var f = open("log.txt", fmWrite)
  defer: f.close()
  f.writeLine("entry 1")
  f.writeLine("entry 2")

when isMainModule:
  writeLog()

Read a File Line-By-Line

for line in lines("data.txt"):
  echo line

Generics

Generic Types and Procedures {.col-span-3}

type
  Box[T] = object
    value: T

proc wrap[T](x: T): Box[T] =
  Box[T](value: x)

let a = wrap(10)        # Box[int]
let b = wrap("hello")   # Box[string]

echo a.value            # 10
echo b.value            # hello

Templates and Macros

Templates

# Template = macro-like, expanded at compile time
template times2(x: untyped): untyped = (x) * 2
echo times2(21)  # 42

Macros {.row-span-2} {.col-span-2}

import std/macros

macro dbg(expr: untyped): untyped =
  # prints both the expression and its value
  result = quote do:
    echo astToStr(`expr`), " = ", `expr`

let x = 10
dbg(x * 2)
# prints: x * 2 = 20
Bookmark This Page Now!