Replace Nil Checks with Nim's option[T]
Make missing values explicit at compile time using Nim’s standard library option[T] type.
03 Nov 2026, 11:41 UTC

The problem: silent nil assumptions
In Nim, a procedure that looks up a value often returns a pointer or reference type, and absence is signaled with nil. The compiler lets you pass that result straight into another procedure without checking. At runtime, dereferencing nil can raise an exception or produce undefined behavior depending on the target. The bug is not that nil exists; it is that nothing forces the caller to acknowledge it might be there.
The takeaway: make absence part of the type
Nim’s standard library provides option[T] in the options module. It is a discriminated union: a value is either some(T), carrying a value of type T, or none(), representing absence. Because option[T] is a plain Nim type, the type checker requires you to inspect it before extracting the inner value. A function that returns option[string] tells every caller, in its signature, that the result might be missing.
How option[T] works
Import the module with import options. Construct a present value with some(42) and an absent one with none[int](). Extract the value with pattern matching:
case opt of
some(x): echo x
none(): echo "missing"
You can also use orElse(default) to supply a fallback, or get() to unwrap immediately. get() raises an exception if the option is none(), so it is safe only when presence has already been proven.
Worked example: a lookup that cannot be forgotten
Suppose you maintain a small in-memory registry of configuration keys. Instead of returning nil when a key is missing, return option[string].
import options, tables
type
Registry = Table[string, string]
proc lookup(reg: Registry, key: string): option[string] =
if reg.hasKey(key):
return some(reg[key])
return none()
var reg: Registry = {}
reg["name"] = "app"
reg["port"] = "8080"
case lookup(reg, "name"):
some(value): echo "Found: ", value
none(): echo "Key not present"
case lookup(reg, "missing"):
some(value): echo "Found: ", value
none(): echo "Key not present"
Run this from a terminal with nim c -r lookup.nim. No special permissions are required. The placeholder lookup.nim is your source file. If the compiler accepts the code, both branches are reachable at runtime: one prints the value, the other prints the absence message. A risk is changing some(value) to value.get() without a guard; if the option is none(), the program raises an exception at the get() call.
Because the result type is option[string], omitting the none() branch can produce a compile-time warning or error depending on compiler settings and Nim version. Check your installed compiler’s exhaustiveness behavior by compiling a case statement that deliberately omits one branch.
Trade-offs and limitations
- Allocation and indirection. Wrapping a value in
option[T]can add heap allocation and pointer chasing compared with a raw value or nilable reference. For hot loops, measure before converting every return type. - C interop.
option[T]is a Nim type. It does not map directly to a C nullable pointer, so you must convert manually at FFI boundaries. - Unsafe unwrapping.
get()onnone()raises. PrefercaseororElseat call sites. - Version drift. Standard-library signatures can differ between Nim patch releases and the devel branch. Inspect
std/options.nimin your Nim installation to confirm the exact procs available.
Practical check
Create a file named option_check.nim importing options, declare a proc returning option[int], compile with nim c -r option_check.nim, and exercise both the some and none paths. Use nim check or your project’s test runner to confirm that pattern matching on option[T] warns when a branch is missing. If you are on a non-standard Nim installation, locate the standard library source to verify the module API before adopting it widely.
Closing
Start by changing return types for lookups and parsers where nil currently means "not found." Use case or orElse at call sites, and reserve get() for code paths that have already proven presence. The compiler will not catch every logic error, but it will catch the forgotten nil check.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.