Wednesday, January 22, 2020

“How Swift Achieved Dynamic Linking Where Rust Couldn’t” ↦

Alexis Beingessner digs into some of the details of Swift’s recent effort to achieve ABI stability and contrasts that to Rust:

For those who don’t follow Swift’s development, ABI stability has been one of its most ambitious projects and possibly it’s defining feature, and it finally shipped in Swift 5. The result is something I find endlessly fascinating, because I think Swift has pushed the notion of ABI stability farther than any language without much compromise.

[…]

Also for context on why I’m writing this, I’m just naturally inclined to compare the design of Swift to Rust, because those are the two languages I have helped develop. Also some folks like to complain that Rust doesn’t bother with ABI stability, and I think looking at how Swift does helps elucidate why that is.

What is ABI stability and why is it important?

When the Swift developers talk about “ABI Stability” they have exactly one thing in mind: they want native system APIs for MacOS and iOS to be written in Swift, and for you to dynamically link to them. This includes dynamically linking to a single system-wide copy of the Swift Standard Library.

Ok so what’s dynamic linking? For our purposes it’s a system where you can compile an application against some abstract description of an interface without providing an actual implementation of it. This produces an application that on its own will not work properly, as part of its implementation is missing.

To run properly, it must tell the system’s dynamic linker about all of the interfaces it needs implementations for, which we call dynamic libraries (dylibs). Assuming everything goes right, those implementations get hooked up to the application and everything Just Works.

Dynamic linking is very important to system APIs because it’s what allows the system’s implementation to be updated without also rebuilding all the applications that run on it. The applications don’t care about what implementation they get, as long as it conforms to the interface they were built against.

[…]

So dynamic linking is our goal, and ABI stability is just a means to that end.

I’ll be honest and say that I don’t really follow all of the details here. I’ve never been much of a compiler nerd. My takeaway is that ABI stability is hard. Really hard. So hard, in fact, that even though Apple has wanted Swift to be ABI-stable from the beginning, it took 5 years and 5 major versions of Swift to achieve.

There are some features of Swift’s design and approach to compilation that lend themselves well to the goal of ABI stability. Even so, ABI stability and dynamic linking are complex goals even under the best of circumstances. I think this final thought from the article sums it up nicely:

Rust actually originally tried a polymorphic design similar to Swift’s, but they eventually backed off from it once the difficulties became clear. Supporting both polymorphic and monomorphic compilation helped Swift a lot, but I think the key difference was ultimately just that Apple had a more significant motivation than Mozilla to pursue dynamic linking and way more resources to throw at this very hard problem.