Nim! pt9 - Pointer Fun

Had a chance to learn about pointer usage in Nim

up

2014-11-16

In my last post on Nim, I talked about how I was learning to use generics. This time, I’d like to talk a bit about pointers!

The first thing to know is that there are two different types of pointer – the “safe” and “unsafe” kind. The “safe” one is traced (in Nim parlance) and managed by a garbage collector. The untraced (“unsafe”) pointers are expected to be entirely managed by the user – these are very useful for accessing hardware, or interoping with non-Nim code.

The seocnd thing to know is that you really don’t need them unless you are, in fact, interoping with C code, accessing hardware, or have a very specific use-case. Of course I didn’t know that until after writing a few hundred lines of code and then writing up the first draft of this article…

In my search for some questions I came up with in the first draft, I came across this, which explained the folly of my ways (well, not directly, but I was able to think through the problems I was solving with a better understanding).

But, awesome! Now I have a much better grasp on when and when not to use Nim’s pointer constructs.

Here’s the equivalent of some code I was trying to write (note: this is the bad example of how to use pointers – not that this example won’t work, it’s just not a place you need to use pointers):

type
  TBaz = object
    y: int
  PBaz = ref TBaz
  TFoo = object
    baz: TBaz
  PFoo = ref TFoo

proc init_new(foo: var PFoo; y: int) =
  foo = PFoo()
  new(foo.baz)  
  foo.baz.y = y

proc baz_accessor(f:var PFoo): var PBaz =
  return f.baz
proc `baz_accessor=`(f:var PFoo, baz: PBaz) =
  f.baz.y = baz.y
  discard """and I was doing some other stuff here (which is why I had the 'baz_accessor' property at all"""

var bar: PFoo
PFoo.init_new 1
assert(bar.baz_accessor.y == 1)
assert(bar.baz.y == 1)
bar.baz_accessor = 10
assert(bar.baz_accessor.y == 10)
assert(bar.baz.y == 10)

And here’s some code whose end result is the same, without pointers (and without the “old” syntax, which I also found out about from the aboved linked article…):

type
  Baz = object
    y: int
  Foo = object
    baz: Baz

proc baz_accessor(f:var Foo): var Baz =
  result = f.baz
proc `baz_accessor=`(f:var Foo, y: int) =
  f.baz.y = y
  discard """and I was doing some other stuff here (which is why I had the 'baz_accessor' property at all"""

var bar = Foo(baz: Baz(y:1))
assert(bar.baz_accessor.y == 1)
assert(bar.baz.y == 1)
bar.baz_accessor = 10
assert(bar.baz_accessor.y == 10)
assert(bar.baz.y == 10)

The first example (the one that uses pointers) uses 26 lines and 581 characters.

The second example (the seemingly correct one) uses 18 lines and 445 characters.

Not a huge difference for this particular example, but as code grows I have first hand knowledge that it becomes a bigger difference! The second, correct example is clearly more concise and readable. It also has no actual need for the init_new method, like the pointer approach really does need. Certainly if initialization was a more important aspect of the Foo type, then the init_new method would have stayed in the second example.

On-wards and upwards! I have some code to clean up now… but this whole process has been enlightening.

2014-11-19 Addendum: After posting this article, I received a nice message from this guy regarding a couple of things: