☰

# std/wrapnils

Theme:

- Manual
- Standard library
- Index
- Compiler docs
- Fusion docs
- devel, stable

Search:
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

# Imports

macros, options

# 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