尼姆:如何证明不是零?

Nim: How to prove not nil?

对我来说,Nim 最有趣的功能之一是 not nil 注释,因为它基本上允许在编译器的帮助下完全静态地排除各种 NPE/访问冲突错误。但是,我很难在实践中使用它。让我们考虑一个最基本的用例:

type
  SafeSeq[T] = seq[T] not nil

这里的一个直接陷阱是即使实例化这样一个 SafeSeq 也不是那么容易。尝试

let s: SafeSeq[int] = newSeq[int](100)

失败并出现错误 cannot prove 'newSeq(100)' is not nil,这令人惊讶,因为人们可能认为 newSeq 根本不是 nil。解决方法似乎是使用这样的助手:

proc newSafeSeq*[T](size: int): SafeSeq[T] =
  # I guess only @[] expressions are provably not nil
  result = @[]
  result.setlen(size)

下一个问题出现在尝试用 SafeSeq 做某事时。例如,人们可能期望当您映射到 SafeSeq 时,结果应该不会再次为 nil。然而,这样的事情也失败了:

let a: SafeSeq[int] = @[1,2,3]
let b: SafeSeq[string] = a.mapIt(string, $it)

一般的问题似乎是一旦 return 类型变成普通的 seq 编译器似乎忘记了 not nil 属性 并且不能再证明一下。

我现在的想法是引入一个小的(可以说是丑陋的)辅助方法,它允许我实际证明 not nil:

proc proveNotNil*[T](a: seq[T]): SafeSeq[T] =
  if a != nil:
    result = a # surprise, still error "cannot prove 'a' is not nil"
  else:
    raise newException(ValueError, "can't convert")

# which should allow this:
let a: SafeSeq[int] = @[1,2,3]
let b: SafeSeq[string] = a.mapIt(string, $it).proveNotNil

然而,编译器在这里也无法证明not nil。我的问题是:

注意:我知道 seq 试图成为 nil 不可知论者,即 一切正常 即使在 nil 情况下也是如此。但是,这仅适用于 Nim 中的 。当连接 C 代码时,零隐藏原则成为错误的危险来源,因为 nil 序列仅在 Nim 端无害...

使用 isNil 魔术检查 nil:

type SafeSeq[T] = seq[T] not nil
proc proveNotNil[T](s: seq[T]): SafeSeq[T] =
  if s.isNil: # Here is the magic!
    assert(false)
  else:
    result = s
let s = proveNotNil newSeq[int]()