What is the Nim way of thinking here?
Say I have a proc that generates a large data structure.
If I use ref as return value how should I write it?
proc CreateData(): ref seq[tuple[x, y: int]] =
result = new seq[tuple[x, y: int]]
result[].add((x:1, y:2))
proc createData(): seq[tuple[x, y: int]] =
result = @[]
result.add((x:1, y:2))
You made me curious, so I checked the generated C code. A pointer to the sequence is returned anyway, so no copies to worry about:
N_NIMCALL(TY89005*, createdata_89003)(void) {
TY89005* result;
result = 0;
result = (TY89005*) newSeq((&NTI89005), 0);
result = (TY89005*) incrSeq(&(result)->Sup, sizeof(TY89006));
result->data[result->Sup.len-1] = TMP12;
return result;
}
Regarding parameters, Nim does the 'right' thing, and passes pointers behind the scenes (even for object types). This can be controlled by pragmas, but that isn't really needed for average use.
I don't know the specifics about return types, but I presume the same thing is done there too.
Nice to know.
A few more questions pop into mind:
result.add((x:1, y:2))
and when I tried the following I got the magical "Illegal storage access" error at runtime when accessing the add proc, even though it complains with a type missmatch at compile time if I leave the []-dereferencing out.
proc CreateData(): ref seq[tuple[x, y: int]] =
result = new seq[tuple[x, y: int]]
result[].add((x:1, y:2))
I guess that it is always allocated on the heap since a pointer is returned, or does it handle fixed length arrays differently?
Yes, it's on the heap. I think you can consider seqs as ref-like objects already.
When I let the return value be a ref, I couldn't do the following, even though I was of the impression that dot (.) should be auto-dereferencing?
Auto-dereferencing only happens when accessing members, see this example:
type Foo = object
x: int
proc bar(a: Foo) = echo a.x
var a: Foo
echo a.x
a.bar
var b: ref Foo = new Foo
echo b.x # auto-dereferences
b[].bar # doesn't auto-dereference
and when I tried the following I got the magical "Illegal storage access" error at runtime when accessing the add proc
Because the seq itself also needs to be initialized:
proc createData(): ref seq[tuple[x, y: int]] =
result = new seq[tuple[x, y: int]]
result[] = @[]
result[].add((x:1, y:2))
Ah, thanks for those explanations, @def.
I've also been experimenting with ref seq recently, and I was also confused as to the correct incantation to avoid "Illegal storage access" (I was doing things in macros at compile-time, so I assumed it was some limitation of the compile-time VM) and I also hadn't worked out why auto-dereferencing didn't seem to happen sometimes.