When doing manual memory management in Nim, I often find myself having to define two versions of a proc - one that takes a var parameter and one that takes a ptr parameter.
Otherwise I will run into issues like this:
var m: ptr Monster
var b: Bullet
proc chooseAnim(m: var Monster) =
# some logic to set the current animation
# ...
proc update(b: ptr Bullet) =
# move the bullet and check for collisions
# ...
m.chooseAnim() # uh oh!
m[].chooseAnim() # better.
b.update() # uh oh!
(addr b).update() # better.
I was happy to discover that {.experimental: "implicitDeref".} is a thing, which pretty much fixes all my problems! If I define the var version of the procedure, I don't need to define the ptr version anymore.
But I suppose I can't start doing this in library code yet, because then I'd be forcing other people to use this experimental pragma in their code, or to have to worry about addressing/dereferencing their stuff to make the library functions happy.
So I was wondering, what's the future of this feature? Do we know yet if it's here to stay? Does it have any caveats that I should know about?
Thanks :)
We didn't develop it further as there were unforeseen interactions where p != nil would be rewritten to p[] != nil or p != q to p[] != q[] but tbh I don't remember the details.
This feature needs to be re-designed. Here is my current proposal:
"If overloading resolution in a call using the syntax a.b or a.b(X) produces no matching candidate for b, the expression is rewritten to a[].b (or a[].b()) and overloading resolution is tried once again"
This rule seem to be more consistent with what Nim does for field access and seems to avoid weird feature interaction. If the rule allows for custom overloaded deref operators it would also improve custom smart pointers because these can then be written without converter which is an unloved feature of Nim. Will write an RFC.
If I define the var version of the procedure, I don't need to define the ptr version anymore.
But the two have different domains (in the functional sense) — the ptr parameter can take nil, while the var parameter can't. It seems wrong for the compiler to produce an illegal behavior by implicitly dereferencing a possibly-nil pointer and passing it to a function that will crash when it accesses it.
In my experience, such crashes can be hard to debug. Both because your reasoning can be wrong ("well, chooseAnim must have crashed because of this other pointer variable, not because of m, because m isn't a pointer...") and because you can sometimes pass this bogus nil var down into further levels of the call stack before some unlucky proc gets around to dereferencing it, so the crash can be pretty far away from the source of the bug.
The proc-vs-var issue you're bringing up is the same as "*" vs "&" in C++. Both have valid purposes. You can either consistently use one or the other, or get used to sprinkling "&" and "*" in your function calls … the latter is often a code smell that you need to rethink the API.