默认情况下,模型是按值传递数据的。当您创建特定类型的 var 时,编译器将在堆栈上为该变量分配所需的空间。这是意料之中的,因为 Nim 编译为 C,而复杂类型只是结构。但就像在 C 或 C++ 中一样,您也可以拥有指针。有ptr
获取不安全指针的关键字,主要用于与 C 代码的接口,还有ref
获取垃圾收集的安全引用(均记录在引用和指针类型中)部分)。
但是,请注意,即使您指定 aproc
按值传递变量,如果编译器认为它可以加快执行速度并且同时是安全的,它也可以自由决定通过引用在内部传递它。实际上,我使用引用的唯一一次是当我将 Nim 类型导出到 C 并且必须确保 C 和 Nim 都指向相同的内存时。请记住,您始终可以检查nimcache
目录中生成的 C 代码。然后您将看到var
proc 中的参数只是指向其 C 结构的指针。
这是一个类型的示例,该类型具有要在堆栈上创建并按值传递的构造函数,以及相应的指针,如版本:
type
Person = object
age: int
name: string
proc initPerson(age: int, name: string): Person =
result.age = age
result.name = name
proc newPerson(age: int, name: string): ref Person =
new(result)
result.age = age
result.name = name
when isMainModule:
var
a = initPerson(3, "foo")
b = newPerson(4, "bar")
echo a.name & " " & $a.age
echo b.name & " " & $b.age
如您所见,代码基本相同,但存在一些差异:
- 区分初始化的典型方法是将init用于值类型,将new用于引用类型。另外,请注意 Nim 自己的标准库错误地使用了这个约定,因为一些代码早于它(例如 newStringOfCap 不返回对字符串类型的引用)。
- 根据您的构造函数实际执行的操作,该
ref
版本允许您返回一个nil
值,您可以将其视为错误,而值构造函数强制您引发异常或更改构造函数以使用下面提到的var 形式,以便您可以返回表示成功的布尔值。失败往往以不同的方式对待。
在类 C 语言中,有一种显式语法可以访问指针的内存值或指针指向的内存值(解除引用)。在 Nim 中也有,它是空下标符号 ( []
)。但是,编译器将尝试自动放置这些以避免混乱代码。因此,该示例不使用它们。为了证明这一点,您可以将代码更改为:
echo b[].name & " " & $b[].age
这将按预期工作和编译。但是以下更改将产生编译器错误,因为您无法取消引用非引用类型:
echo a[].name & " " & $a[].age
Nim 社区当前的趋势是摆脱单字母前缀来区分值和引用类型。在旧约定中,您将有一个TPerson
和一个参考值的别名作为PPerson = ref TPerson
. 你会发现很多代码仍然使用这个约定。
- 取决于你的对象和构造函数需要做什么,而不是
initPerson
返回值,你也可以有一个init(x: var Person, ...)
. 但是隐式变量的使用允许编译器对此进行优化,因此将 a 传递给调用者result
更像是一种口味偏好或要求。bool