这个问题主要涉及到函数传值,但具体是传值还是传引用对于 Nim 来说并不好说,编译器会尽量采用更高效的方式

如果选用不同的 GC 策略,那么可能又是另一回事(比如 ARC/ORC 可能根据上下文来说就是移动语义)

下面是个例子(基于 2.0 之前的版本):

type
  Person = object
    name: string
    age: Natural

proc intro(person: Person) =
  echo person.repr

when isMainModule:
  var p = Person(name: "John", age: 100)
  intro(p)

上面我们定义了一个 Person 类型,我们可以通过 sizeof 来查看它的大小:

echo "sizeof(Person): " & $sizeof(Person) & " Bytes"

结果如下:

sizeof(Person): 24 Bytes

可以看到当前的 Person 相对来说还是比较小的

接下来我们看一下调用 intro 时,变量 p 是如何传递的(下面是 C 代码的表示,为了方便看,我删掉了 mangle 的内容):

: 我使用的官方编译器,所以会先编译出 C 代码,我配置的 C 编译器是 clang (如果你使用 nlvm 走 LLVM-IR 路线, 那么这篇文章可能不是很适合这种情况,这里我们只讨论 nimc 的表现情况及结果)

static const NimStringV2
name = {4, (NimStrPayload*)&{ 4 | NIM_STRLIT_FLAG, "John" }};

nimZeroMem((void*)(&p), sizeof(Person));
p.name = name;
p.age = ((NI) 100);
intro(p);

可以看到这里变量 p 的传递直接进行了一次拷贝

但如果我们对 Person 进行下面的修改:

type
  Person = object
    name: string
    age: Natural
+   padding {.used.}: array[512, int]

我们加入了一个 padding 字段,使现在 Person 的大小变成了 sizeof(Person): 4120 Bytes

再看一下生成的 C 代码:

static const NimStringV2
name = {4, (NimStrPayload*)&{ 4 | NIM_STRLIT_FLAG, "John" }};

nimZeroMem((void*)(&p), sizeof(Person));
p.name = name;
p.age = ((NI) 100);
intro((&p));

可以看到这里只是传递了 p 的地址

所以 Nim 参数的传递还是比较高效的