However, I must ask: is there a world in which interop didn't have to be so bad without hugely sacrificing memory safety? Is this a problem that Zig solves (I must be honest, I don't really know much about Zig)? What _is_ the better solution?
However, I must ask: is there a world in which interop didn't have to be so bad without hugely sacrificing memory safety? Is this a problem that Zig solves (I must be honest, I don't really know much about Zig)? What _is_ the better solution?
The hard part is in translating C code's high-level safety requirements into Rust APIs that enforce them. I'm talking about requirements that aren't expressed in the C syntax, but are arbitrary domain-specific rules defined in English by the C code's authors ("this function can be called only on Wednesdays"). These turn out to be hard, because they may not be precisely defined, and/or the conditions are complex and implementation-specific. That's less of language inteop problem, and more in creating formal definitions of informal documentation.
Having said that, there are a couple of things that Rust made harder for itself:
• Rust allows moving objects to a different address safely. C code often assumes objects never move and their addresses are unique and meaningful. If Rust had built-in support for immovable types, it wouldn't need Pin and macro hacks.
• Rust's reference types require strict immutability or strict pointer aliasing (exclusive access), while C allows memory to be mutated from anywhere, and pointers to const don't mean the data is immutable. This prevents Rust from using its nice safe reference and slice syntax on memory externally mutated in surprising/clever ways (by memory-mapped hardware, shared mem, MMU trickery).
You can't really make interop nicer than the lowest common denominator, and when one of the sides is C then that lowest common denominator is very low. Rust is one of very few languages that can potentially interop with memory-safe languages without having to completely buy into a common runtime - e.g. it's increasingly popular to write Python extensions in Rust and I believe there are now libraries for doing that without having to go via C - and when you have richer abstractions available on both sides of the line then you can make use of them. But to work with C you have to work like C, at least at the boundary.
Yes and no. Unix has a very C-oriented ABI because it's written in and tied to C, and since Unix and C won other operating systems have felt pressure to make their ABI C-friendly.
> we could just as well be stuck with Pascal calling conventions instead, which was used by 16-bit Windows versions
If we'd gotten Pascal strings in the ABI that alone would have eliminated one major class of bugs.
There is a subset of Zig types that is compatible with C APIs, but most higher-level Zig types (like slices, odd-width integer types, optionals, error unions...) can't be tunneled through or mapped to a C API...
The main thing may be that the Zig compiler doesn't track ownership/lifetimes like the Rust compiler does, so it doesn't matter that this information would be lost at the C-API barrier.
Could you elaborate? I find the interop to be quite nice. I mean, obviously, interop between any two languages cannot ever be "better" than the least good language at any one thing.
> for example calling directly from Rust into C without any expression of the semantics means you have no mechanism for reasoning about ownership
Right. But since C cannot express Rust's ownership semantics, could one really do better? Does that mean that as long as there's significant amounts of C in Linux, then Linux should never introduce any language with stronger semantics than C? That seems unfortunate.
> and that removes many of the security benefits of writing code in Rust in the first place.
I've always thought of the idea being that _as long as the necessary manual reasoning is correct_, downstream Rust users _do_ get those benefits. But of course interfacing with C, lacking the necessary semantics, requires the interface writer to manually assure them. Wouldn't anything more demand more than C can deliver?
> If you have a strong description of the semantics of the C code then you can write Rust bindings that expose that, but you're then constraining the maintainers of the C codebase in terms of their ability to make arbitrary changes without considering the impact that has on the Rust consumers.
OK, but that seems like a political/social/organizational aspect far more than a technical problem with C-Rust-interop?
(To be clear I have spent many nights drinking with Christoph and think he's entirely in the wrong here)
The issues with C FFI are comparatively minor and not at all unique to Rust. Hell, even C++ has those issues when interacting with C APIs. It's not like a std::variant magically becomes a tagged union.
That's what I meant but poorly expressed (e.g. C++ <=> C interop has roughly similar problems as Rust <=> C interop).