1. 3
    1. 2

      Off the top of my head, I can’t think of any functions that take fixed size arrays. Even crypto code which only works on fixed size blocks always takes slices for convenience.

        1. 2

          Ah, there it is. :)

    2. 1

      We use runtime.SetFinalizer to inform the runtime about our object and what to do when it drops out of scope.

      The article was nice and informative. This snippet is a bit inaccurate though, finalizers are not called when an object goes out of scope, but when the object becomes unreachable. Moreover, it is not guaranteed that the finalizer runs before the program ends:

      The finalizer is scheduled to run at some arbitrary time after the program can no longer reach the object to which obj points. There is no guarantee that finalizers will run before a program exits, so typically they are useful only for releasing non-memory resources associated with an object during a long-running program.

      Source: https://golang.org/pkg/runtime/#SetFinalizer

      1. 2

        That advice in the docs about not using it for memory related resources is understandable, but it’s not really true in practice, particularly when binding to a C library. In most (every?) case I’ve seen, finalizers are used to call C.free. (As is also done in the OP.)

        1. 1

          I think in most cases people provide a method for explicitly freeing memory and the finalizer is mostly a ‘safety net’ in case someone forgets to explicitly free the resources.

          Edit: Silently freeing in finalizers may actually be the worst approach, because it does not uncover missing Close()s. It would probably be much better to verify in the finalizer that a resource was properly closed and complain about it if it isn’t. Same for finalizers for other objects (files, etc.), since relying on finalizers or forgetting to close resources properly may lead to resource exhaustion (e.g. running out of file descriptors).

        2. 1

          Just wanted to add to my own sibling comment that this is actually quite dangerous, because people may have wrong intuitions about reachability. E.g. most people think that reachability of a local variable is tied to its scope, which is not the case in Go. For example, it is easy to let the the code from the article crash with a segmentation fault:

          https://play.golang.org/p/VubeL_GWNvO

          In this case I forcefully garbage collect, but it is easy to imagine calling some other function in runtime.GC()s place that allocates some memory and triggers garbage collection. To prevent a segmentation fault, you have to explicitly insert a runtime.KeepAlive(object) after the last use.

          Finalizers should typically be avoided, unless you know what you are doing & it does not result in leaky abstractions that are hard to use/discover for downstream users.

      2. 1

        You are correct. I meant to say it would catch some of the freeing for you if you yourself had forgotten to do so.

        1. 2

          By the way, the code in the article actually never runs the finalizer on my machine (Go 1.12.1), even with forced GC. I had to change:

          runtime.SetFinalizer(buf, func(_ *buffer) {
              go buf.free()
          })
          

          to:

          runtime.SetFinalizer(buf, func(buf *buffer) {
              go buf.free()
          })