对我而言,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
根本不是零。解决方法似乎使用这样的帮助:
proc newSafeSeq*[T](size: int): SafeSeq[T] =
# I guess only @[] expressions are provably not nil
result = @[]
result.setlen(size)
尝试使用SafeSeq
执行某项操作时会出现下一个问题。例如,人们可能期望在映射SafeSeq
时,结果应该不再为零。但是,这样的事情也失败了:
let a: SafeSeq[int] = @[1,2,3]
let b: SafeSeq[string] = a.mapIt(string, $it)
一般问题似乎是,只要返回类型变为普通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
。我的问题是:
在这种情况下,如何帮助编译器推断not nil
?
此功能的长期目标是什么,即是否计划推断not nil
更强大?手动proveNotNil
的问题在于它可能不安全,并且反对编译器负责证明它的想法。但是,如果只在非常罕见的情况下才需要proveNotNil
,那就不会造成太大伤害。
注意:我知道seq
尝试成为nil
不可知的,即一切正常即使在nil
情况下也是如此。但是,这仅适用于Nim 中的。当连接C代码时,nil-hidden-principle成为bug的危险源,因为nil
序列在Nim端只是无害的......