std/wrapnils

  Source   Edit

This module allows evaluating expressions safely against the following conditions:

  • nil dereferences
  • field accesses with incorrect discriminant in case objects

default(T) is returned in those cases when evaluating an expression of type T. This simplifies code by reducing need for if-else branches.

Note: experimental module, unstable API.

Example:

import std/wrapnils
type Foo = ref object
  x1: string
  x2: Foo
  x3: ref int

var f: Foo
assert ?.f.x2.x1 == "" # returns default value since `f` is nil

var f2 = Foo(x1: "a")
f2.x2 = f2
assert ?.f2.x1 == "a" # same as f2.x1 (no nil LHS in this chain)
assert ?.Foo(x1: "a").x1 == "a" # can use constructor inside

# when you know a sub-expression doesn't involve a `nil` (e.g. `f2.x2.x2`),
# you can scope it as follows:
assert ?.(f2.x2.x2).x3[] == 0

assert (?.f2.x2.x2).x3 == nil  # this terminates ?. early

Example:

import std/wrapnils
# ?. also allows case object
type B = object
  b0: int
  case cond: bool
  of false: discard
  of true:
    b1: float

var b = B(cond: false, b0: 3)
doAssertRaises(FieldDefect): discard b.b1 # wrong discriminant
doAssert ?.b.b1 == 0.0 # safe
b = B(cond: true, b1: 4.5)
doAssert ?.b.b1 == 4.5

# lvalue semantics are preserved:
if (let p = ?.b.b1.addr; p != nil): p[] = 4.7
doAssert b.b1 == 4.7

Procs

func `[]`[T, I](a: Option[T]; i: I): auto {.inline.}
See top-level example.   Source   Edit
func `[]`[U](a: Option[U]): auto {.inline.}
See top-level example.   Source   Edit

Macros

macro `?.`(a: typed): auto
Transforms a into an expression that can be safely evaluated even in presence of intermediate nil pointers/references, in which case a default value is produced.   Source   Edit
macro `??.`(a: typed): Option
Same as ?. but returns an Option.

Example:

import std/options
type Foo = ref object
  x1: ref int
  x2: int
# `?.` can't distinguish between a valid vs invalid default value, but `??.` can:
var f1 = Foo(x1: int.new, x2: 2)
doAssert (??.f1.x1[]).get == 0 # not enough to tell when the chain was valid.
doAssert (??.f1.x1[]).isSome # a nil didn't occur in the chain
doAssert (??.f1.x2).get == 2

var f2: Foo
doAssert not (??.f2.x1[]).isSome # f2 was nil

doAssertRaises(UnpackDefect): discard (??.f2.x1[]).get
doAssert ?.f2.x1[] == 0 # in contrast, this returns default(int)
  Source   Edit

Templates

template fakeDot(a: Option; b): untyped
See top-level example.   Source   Edit