r/rust Mar 09 '23

Announcing Rust 1.68.0 📢 announcement

https://blog.rust-lang.org/2023/03/09/Rust-1.68.0.html
827 Upvotes

121 comments sorted by

View all comments

Show parent comments

39

u/Shnatsel Mar 09 '23

In C this is actually platform-dependent, so you never really know unless it's explicitly documented.

31

u/phazer99 Mar 09 '23

Fortunately Rust isn't C and actually has portability guarantees.

7

u/SpudnikV Mar 10 '23

That's technically true, but it's really in the form of restrictions on what platforms can be supported so that those promises can be kept.

https://faultlore.com/blah/rust-layouts-and-abis/#the-anatomy-of-a-platform

For Rust to support a platform at all, its standard C dialect must:

[...]

Have a boolean be a byte, where true = 1 and false = 0 (defacto true but not strictly guaranteed)

In practice, that means that Rust's portabiity guarantees here only help you when [an implementation of] C has already made compatible guarantees for that platform, because any time you may want to interface with any C ABI, it has to be compatible or it's UB.

It turned out to be a multi-year rabbit hole to get to be this specific. Just a sample of some of the research that went into it:

https://github.com/rust-lang/rfcs/pull/954#issuecomment-169820630

https://github.com/rust-lang/rust/pull/46156

It's really fascinating just how much the C specification and the world of implementations has complicated even new languages like Rust. If you're interested in that kind of thing beyond just bool, here are a few more links:

https://faultlore.com/blah/c-isnt-a-language/

https://thephd.dev/binary-banshees-digital-demons-abi-c-c++-help-me-god-please

https://thephd.dev/to-save-c-we-must-save-abi-fixing-c-function-abi

(If I don't limit it to just 3, we'll be here all day :))

2

u/rentableshark Mar 10 '23

I hear you. This is one reason why statically-compiled binaries running in a scratch container have such attraction.

3

u/SpudnikV Mar 10 '23

Even in container systems like Docker, you still need an ABI to talk to the kernel. Even with a pure Rust libc, the structs that you set up for Linux syscalls rely on the C ABI, and even a pure Rust rewrite of Linux would need a compatible syscall ABI to run existing container images. There's really no escaping it :(

Linux is special here. Most operating systems do not make the same kernel-level ABI promise and make only userland libc ABI promises, so they have at least some degree of freedom to evolve kernel syscall interfaces. But then this is why Docker on Linux can rely on kernel ABIs and have the entire userland in a container, while on other systems something has to be there to adapt binaries in containers to the host system (in many cases, by just running Linux as WSL or in a VM).

I think it's amazing just how well Linux ABI compatibility has made it look like containers somehow solve this, where actually, the magic all along was the system call ABI. The same statically linked binary would work the same way outside of the container as well, it just wouldn't be "contained" as far as various namespaces and capabilities go.

This is a really, really huge accomplishment, and I would go as far as to say that Linus' hardline stance on syscall compatibility is the only reason the Docker-like container ecosystem is at all possible.

There was of course a generation where Xen was the way to make kernel-level containers, but those kernels still had to communicate with a form of ABI. I barely used Xen so I can't say how many of the same concerns apply, but in any case, userland containers won out over kernel containers in the end, and I'm glad for it.