Swift Regret: OpaquePointer

Part of the Swift Regrets series.

OpaquePointer is the type used by Swift for any pointer to a forward-declared C struct with no definition. This is a pretty common C idiom for library evolution and encapsulation…that Swift just drops on the floor.

How did we get here? From the start we knew Swift wasn’t going to have forward declarations. They’re a nightmare to implement and not great to use, even if they speed up compilation in C vs. including a header. In most cases that’s fine, because you can’t use such a type anyway. (Though the compiler just drops such declarations instead of importing them as “unavailable” for annoying, edge-case-y, possibly-solvable reasons.)

But pointers are an exception. There are options here, like importing a forward-declared type as an empty struct. But I (specifically me) was afraid of the case where you import foo_interface.h in an interpreter, synthesize foo_t, and then import foo_impl.h. Would we update foo_t? Replace it? Get conflicting behavior? And live interpreters (REPLs, playgrounds) are only one problem. Debugging also means loading every module in the program. What if now having foo_impl.h around makes some other (Swift) module invalid, cause it previously depended on foo_interface.h?

We ultimately ran out of time, i.e. this wasn’t something that had to be fixed before Swift 1.0. But then we/I didn’t come up with anything later either. So OpaquePointer stuck around, and Swift is less type-safe than C for this particular idiom.

In hindsight, I think I would say “synthesize the struct, treat it like a non-frozen struct with no runtime support, and if the real definition shows up, fill in members like an extension along with size+alignment.” It would have been annoying to implement but it would have 95% worked.

It wouldn’t be better than C. It’d be slightly worse around the edges, I’m sure. But me hoping for something better than C left us all with something worse, and it’d be source-breaking and ABI-breaking to change.