5 ms·
I don't think Rust ever wants to do ABI stability that isn't opt-in, because Rust is about zero-cost abstractions and a stable ABI is not zero-cost. See this ex
by ameliaquining 1y ago
I don't think Rust ever wants to do ABI stability that isn't opt-in, because Rust is about zero-cost abstractions and a stable ABI is not zero-cost. See this explanation of how C++'s de facto stable ABI significantly reduces performance: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p2028r0.pdf https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p20... [PDF]
For the use case of letting Rust programs dlopen plugins that are also written in Rust, the current solutions are stabby and abi_stable. They're not perfect but they seem to work fairly well in practice.
For more general use cases like cross-language interop, the hope is to get somewhere on crABI: https://github.com/joshtriplett/rfcs/blob/crabi-v1/text/3470-crabi-v1.md https://github.com/joshtriplett/rfcs/blob/crabi-v1/text/3470... This is intended to be a superior successor to C's ABI, useful for all the same use cases (at least if the code you want to talk to is new enough to support it). Note that this isn't something the Rust maintainers can do unilaterally; there's a need to get buy-in from maintainers of other languages, Linux distros, etc.
I'm not sure that the "everything is a shared library" approach is workable in a language where heap allocation is generally explicit and opt-in. 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.
- 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.