3 ms·
> While Swift has some support for stack allocation, most types are heap-allocated, and I think that reduces the extent to which the rest of the language has to
by ninkendo 1y ago
> While Swift has some support for stack allocation, most types are heap-allocated, and I think that reduces the extent to which the rest of the language has to be warped to accommodate the stable ABI.
Stack vs heap in swift is simple: structs are stack, classes are heap.
This is what makes Swift’s effort so impressive: it works with stack data just as well as heap. Stack allocated types being used across library boundaries has a ton of plumbing to make it work, there’s the concept of a value witness table (like a vtable but for size/stride information) that can allow looking up sizes at runtime. Stack allocation makes heavy use of alloca(), which most languages shy away from (since it means stack frames aren’t of a static size.) It was difficult but swift managed to do it.
For rust to do something similar it would have to fix a lot of cases where object safety is broken, and yeah, this may require changing the language, particularly to support dynamic/runtime size information. (It would also make the dyn keyword effectively obsolete, because to make generics work across library boundaries, any type has to be able to “become” dynamic if the linker needs it to be.)
It would definitely require changes to the language, but Swift provides some inspiring prior art here.
This article goes into the details: https://faultlore.com/blah/swift-abi/ https://faultlore.com/blah/swift-abi/
- ameliaquining 1y agoYeah, that article is kind of my point; I don't see Rust making all the runtime performance and language-design tradeoffs that it talks about, just to speed up compiles and make easier the not-that-common other use cases for a language-specific stable ABI. (Swift has different tradeoffs because it mostly doesn't target foundational software, and because it specifically targets a niche that prioritizes code size.) Hence why I think a stable ABI would have to be opt-in.
- ninkendo 1y ago> Swift has different tradeoffs because it mostly doesn't target foundational software, and because it specifically targets a niche that prioritizes code size. IMO it goes way beyond code size. Swift needs a stable ABI so that when you update iOS, your apps don't all break. (This "niche" can IMO be described as "anyone writing an OS".) Apps on your device don't just link to C-ABI dylibs, they link to tons of Apple-provided frameworks (including things like UIKit) which provide a rich API surface area, with a ton of interdependent relationships between the app's code and the platform's code. Apps have to implement complicated protocols (traits) which the OS cares deeply about, or sometimes inherit from a platform-provided base class, and these protocols need to stay binary-stable across releases. If disk space were unlimited and apps simply statically linked everything (including UIKit, etc), there'd be no way for Apple to make improvements to these frameworks during an OS release and have apps get the benefits without recompilation. They'd have never been able to do an OS-wide "dark mode", for instance. More than that, if iOS needs to change the implementation of an OS-level API (like the share sheet, or location services, etc), they'd be completely unable to do this if apps all compiled it in statically. [1] But yeah, there's no equivalent of iOS in the rust world. As another commenter pointed out, rust is an orphan without a primary platform, there's no OS provider using rust to provide OS-level API's, so basically nobody needs this right now. But it also means that, if someone _wanted_ to write an OS in Rust and provide rich API's for apps to use (which is the definition I take for "Foundational Software"), this would likely be the first problem they'd need to solve. [1] In reality, many things on iOS are split across an XPC boundary so that the "implementation" is in a separate process written by apple, which is rebuilt in a new OS release, but ironically, the framework's ABI is what shoulders the backwards compatibility burden, not the XPC protocol. Apple changes XPC definitions all the time, they simply rely on the client framework updating in lockstep with the daemon, and for apps to link to the client framework dynamically.
- ameliaquining 1y agoIIUC in Swift 4 and earlier (which didn't have a stable ABI) people just statically linked most of their libraries. This did prevent apps from breaking on OS updates, but was bad for code size. Swift is unique in that both the OS itself and all the apps are written in it, so a stable ABI with very tight language integration makes sense. I'm guessing that a novel Rust OS probably wouldn't work that way, at least if it were aiming to be an open platform; you would want to let people write apps targeting that OS in C or C++ or Go or whatever else. So it would make more sense to use an ABI that was designed from the ground up for cross-language compatibility, like crABI is trying to do (and then it wouldn't make sense for such an ABI to apply to code that didn't opt in).
- ninkendo 1y ago> people just statically linked most of their libraries Correction: People statically linked in other swift libraries. Particularly Swift's stdlib, which finally became usable dynamically circa Swift 5.5. The libraries provided by the OS at the time (UIKit, etc) were all ObjC, and they had stable ABI's (ObjC's ABI is basically string-matching on method names, so it's always had a stable ABI) and so were dynamically linked. People using UIKit from Swift were never statically linking it. > Swift is unique in that both the OS itself and all the apps are written in it This is what I'm driving at by using Rust for "foundational software". To me, foundational software means the API's the OS itself offers could be written in Rust, and could be called from Rust apps using dynamic linking. Right now if we wanted to do this, a C ABI is all we can use, which limits the utility. (Not least because all `extern "C"` in rust is implicitly tagged as unsafe.) > So it would make more sense to use an ABI that was designed from the ground up for cross-language compatibility, like crABI is trying to do (crABI was trying to do this, but it seems dead now.) Something like this would be awesome, and it's what I'm advocating for. I don't even really think we need all of Rust to be able to be used across dynamic linker boundaries... as others have pointed out, allowing enums (with associated values), Option<T> and Result<T, E>, to be passed across ABI boundaries would be a great place to start. I also agree that such an ABI doesn't have to be forced on everyone, it can be opt-in as you suggest, and used for cases where it makes sense.