Fringe Rustdev: Fragmenting the crate ecosystem
We have recently started working on Rust crates to provide two things Rust does not normally provide: unloading safety and signal safety. It started when we wanted our hexchat plugin crate to be truly considered safe Rust. To achieve that, we need it to be such that someone using the crate cannot trigger unsoundness in 100% safe Rust, and because it's a plugin crate, it suffers from the "unloading safety" problem. While we have managed to find a workable solution, yes, it involves fragmenting the crate ecosystem. We argue that's a feature.
Unloading safety
"Unloading safety", in the case of hexchat plugins, mostly boils down to the ability to unload the plugin without crashing. A naive "safe" wrapper that doesn't consider the full implications of the plugin interface would, if the plugin has spawned threads, crash when unloading the plugin. A slightly less naive wrapper would tell hexchat not to unload the plugin, but would still crash when closing hexchat, because hexchat force-unloads plugins on exit.
There are some workarounds such as marking the plugin object as not-able-to-unload, or using musl libc, or other postprocessing steps that fundamentally do not address the elephant in the room: these are all extra steps the user has to take outside the type system/language, and thus must be considered as "soundness requirements" and documented under the "Safety" section of an unsafe function/trait/etc.
We can do better. Threads are generally provided by libstd, the Rust Standard Library, but Rust has two other modes of operation, called "no std" and "no alloc". As it turns out, the Rust Standard Library is actually composed of three parts: libcore, which provides system-independent utilities as well as nearly all language intrinsics (language features implemented as library items), liballoc, which provides heap allocation functionality, including collection types, and libstd, which provides OS interfaces. Conveniently, you can turn them off!
By building on top of "no std", and recognizing that leaking is safe, we can simply remove functionality that's incompatible with unloading safety. Or, ideally, we can provide appropriate alternatives. For example, our hexchat plugin crate could provide its own thread spawning functionality and manage those threads in a way that "just works" with hexchat, while making sure attempts to spawn libstd threads result in compilation failure, obviating the need for "unsafe"!
Signal safety
As it turns out, unloading safety isn't the only thing we can do with this technique. With a few tweaks, and dropping liballoc in addition to libstd (i.e., going "no alloc"; famously, memory allocation is not compatible with signal handlers), we can create a Rust environment that not only provides unloading safety, but also provides signal safety while at it. However, this takes significantly more effort than simple unloading safety, and as a library-only solution comes with a few major drawbacks, being mostly only useful as a tech demo.
(Still, a tech demo can be pretty cool sometimes. But anyway,)
Fragmenting the crate ecosystem?
As the crates built for unloading safety or signal safety use different APIs and safety requirements from crates built for the Rust Standard Library, and they can't be used together, this could eventually lead to fragmenting the entire crate ecosystem in two, or more, large groups: because the Rust Standard Library, and especially liballoc, is fundamental to so many crates out there, these crates would need to be ported for anyone needing signal or unloading safety. This would be an even bigger split than the old splits around Tokio and other async crates.
But is this a bad thing? Among other things, what we have today is that many different groups of ppl use Rust, and they don't use it the same way. In fact, in addition to this signal safety and unloading safety stuff, another non-insignificant group of Rust users is the folks who would like to see more `dyn` in more places. While fragmentation does mean you lose some ability to reuse stuff, it also means you get to try more stuff out and figure out what works better for different use-cases.
(And to a lesser extent, it could be argued as a way for the multiple communities that make up Rust to take control of the language away from the small team of maintainers that fundamentally cannot represent all of these communities of users.)