A(n anti)pattern for "deinitializing" structs(?)

Sure. In fact, it's even used in the standard library.

It doesn't have to call deallocate via indirection. You could replace the closure with a copy of the pointer, like this:

private final class Deinitializer {
    let ptr: UnsafeMutableRawPointer
    init(_ ptr: UnsafeMutableRawPointer) { self.ptr = ptr }
    deinit { ptr.deallocate() }
}

Now what you've created is an "owner" for the memory pointed to by ptr. The fact that you're storing a copy of that pointer value alongside it in a (pointer, owner) pair to avoid double indirection doesn't matter.

A similar pattern is used for shared Strings (which, even though not publicly exposed, are baked in to the ABI). Take a look at implementation of __SharedStringStorage (and the draft implementation to see how it is used). It does a similar thing with (pointer, owner) pairs:

final internal class __SharedStringStorage {
  internal var _owner: AnyObject?
  internal var start: UnsafePointer<UInt8>
  ...
}

Here, as far as the String is concerned, _owner is just "some object I need to retain to keep the memory alive" (and, by implication, something it should release when it no longer needs the memory).

Of course, ManagedBuffer is the preferred way to allocate storage while avoiding double-indirection, but if you have a use-case which can't be modelled with that type, storing a (pointer, owner) pair is a perfectly reasonable implementation strategy IMO.

8 Likes