| ▲ | jcranmer 7 hours ago |
| C currently remains the language of system ABIs, and there remains functionality that C can express that Rust cannot (principally bitfields). Furthermore, in terms of extensions to the language to support more obtuse architecture, Rust has made a couple of decisions that make it hard for some of those architectures to be supported well. For example, Rust has decided that the array index type, the object size type, and the pointer size type are all the same type, which is not the case for a couple of architectures; it's also the case that things like segmented pointers don't really work in Rust (of course, they barely work in C, but barely is more than nothing). |
|
| ▲ | gf000 12 minutes ago | parent | next [-] |
| > that C can express that Rust cannot The reverse is probably more true, though. Rust has native SIMD support for example, while in standard C there is no way to express that. 'C is not a low-level language' is a great blog post about the topic. |
| |
| ▲ | kristianp 7 minutes ago | parent [-] | | So use the fairly common SIMD extensions in C, that's not much of an argument. |
|
|
| ▲ | vlovich123 4 hours ago | parent | prev | next [-] |
| Can you expand on bitfields? There’s crates that implement bitfield structs via macros so while not being baked into the language I’m not sure what in practice Rust isn’t able to do on that front. |
| |
| ▲ | ZeWaka 4 hours ago | parent [-] | | Yeah, not sure what they're saying... I use bitfields in multiple of my rust projects using those macros. | | |
| ▲ | maweki 4 hours ago | parent | next [-] | | I'm not a rust or systems programmer but I think it meant that as an ABI or foreign function interface bitfields are not stable or not intuitive to use, as they can't be declared granularily enough. | |
| ▲ | pjmlp 2 hours ago | parent | prev [-] | | Across binary libraries ABI, regardless of static or dynamically linked? |
|
|
|
| ▲ | raggi 7 hours ago | parent | prev | next [-] |
| That first sentence though. Bitfields and ABI alongside each other. Bitfield packing rules get pretty wild. Sure the user facing API in the language is convenient, but the ABI it produces is terrible (particularly in evolution). |
| |
| ▲ | mjevans 6 hours ago | parent [-] | | I would like a revision to bitfields and structs to make them behave the way a programmer things, with the compiler free to suggest changes which optimize the layout. As well as some flag that indicates the compiler should not, it's a finalized structure. |
|
|
| ▲ | kbolino 6 hours ago | parent | prev | next [-] |
| I'm genuinely surprised that usize <=> pointer convertibility exists. Even Go has different types for pointer-width integers (uintptr) and sizes of things (int/uint). I can only guess that Rust's choice was seen as a harmless simplification at the time. Is it something that can be fixed with editions? My guess is no, or at least not easily. |
| |
| ▲ | jcranmer 6 hours ago | parent | next [-] | | There is a cost to having multiple language-level types that represent the exact same set of values, as C has (and is really noticeable in C++). Rust made an early, fairly explicit decision that a) usize is a distinct fundamental type from the other types, and not merely a target-specific typedef, and b) not to introduce more types for things like uindex or uaddr or uptr, which are the same as usize on nearly every platform. Rust worded in its initial guarantee that usize was sufficient to roundtrip a pointer (making it effectively uptr), and there remains concern among several of the maintainers about breaking that guarantee, despite the fact that people on the only target that would be affected basically saying they'd rather see that guarantee broken. Sort of the more fundamental problem is that many crates are perfectly happy opting out of compiling for weirder platform--I've designed some stuff that relies on 64-bit system properties, and I'd rather like to have the ability to say "no compile for you on platform where usize-is-not-u64" and get impl From<usize> for u64 and impl From<u64> for usize. If you've got something like that, it also provides a neat way to say "I don't want to opt out of [or into] compiling for usize≠uptr" and keeping backwards compatibility. If you want to see some long, gory debates on the topic, https://internals.rust-lang.org/t/pre-rfc-usize-is-not-size-... is a good starting point. | | |
| ▲ | zozbot234 2 hours ago | parent [-] | | > ...not to introduce more types for things like uindex or uaddr or uptr, which are the same as usize on nearly every platform. ... there remains concern among several of the maintainers about breaking that guarantee, despite the fact that people on the only target that would be affected basically saying they'd rather see that guarantee broken. The proper approach to resolving this in an elegant way is to make the guarantee target-dependent. Require all depended-upon crates to acknowledge that usize might differ from uptr in order to unlock building for "exotic" architectures, much like how no-std works today. That way "nearly every platform" can still rely on the guarantee with no rise in complexity. |
| |
| ▲ | aw1621107 6 hours ago | parent | prev [-] | | > Is it something that can be fixed with editions? My guess is no, or at least not easily. Assuming I'm reading these blog posts [0, 1] correctly, it seems that the size_of::<usize>() == size_of::<*mut u8>() assumption is changeable across editions. Or at the very least, if that change (or a similarly workable one) isn't possible, both blog posts do a pretty good job of pointedly not saying so. [0]: https://faultlore.com/blah/fix-rust-pointers/#redefining-usi... [1]: https://tratt.net/laurie/blog/2022/making_rust_a_better_fit_... |
|
|
| ▲ | dataflow 7 hours ago | parent | prev | next [-] |
| In what architecture are those types different? Is there a good reason for it there architecturally, or is it just a toolchain idiosyncrasy in terms of how it's exposed (like LP64 vs. LLP64 etc.)? |
| |
| ▲ | jcranmer 6 hours ago | parent [-] | | CHERI has 64-bit object size but 128-bit pointers (because the pointer values also carry pointer provenance metadata in addition to an address). I know some of the pointer types on GPUs (e.g., texture pointers) also have wildly different sizes for the address size versus the pointer size. Far pointers on segmented i386 would be 16-bit object and index size but 32-bit address and pointer size. There was one accelerator architecture we were working that discussed making the entire datapath be 32-bit (taking less space) and having a 32-bit index type with a 64-bit pointer size, but this was eventually rejected as too hard to get working. | | |
| ▲ | sroussey 2 hours ago | parent [-] | | I guess today, instead of 128bit pointers we have 64bit pointers and secret provenance data inside the cpu, at least on the most recent shipped iPhones and Macs. In the end, I’m not sure that’s better, or maybe we should have had extra large pointers again (in that way back 32bit was so large we stuffed other stuff in there) like CHERI proposes (though I think it still has secret sidecar of data about the pointers). Would love to Apple get closer to Cheri. They could make a big change as they are vertically integrated, though I think their Apple Silicon for Mac moment would have been the time. I wonder what big pointers does to performance. | | |
|
|
|
| ▲ | throw_a_grenade 3 hours ago | parent | prev [-] |
| Also you can't do self-referential strutcs. Double-linked lists are also pain to implement, and they're are heavily used in kernel. |
| |
| ▲ | K0nserv 2 hours ago | parent | next [-] | | > Also you can't do self-referential strutcs. You mean in safe rust? You can definitely do self-referential structs with unsafe and Pin to make a safe API. Heck every future generated by the compiler relies on this. | |
| ▲ | menaerus 3 hours ago | parent | prev [-] | | There's going to be ~zero benefit of Rust safety mechanisms in the kernel, which I anticipate will be littered with the unsafe sections. I personally see this move not as much technical as I see it as a political lobbying one (US gov). IMHO for Linux kernel development this is not a good news since it will inevitably introduce friction in development and consequently reduce the velocity, and most likely steer away certain amount of long-time kernel developers too. | | |
| ▲ | 2 hours ago | parent | next [-] | | [deleted] | |
| ▲ | baq 2 hours ago | parent | prev [-] | | Don’t spread FUD, you can check some example code yourself. https://git.kernel.org/pub/scm/linux/kernel/git/a.hindborg/l... | | |
| ▲ | menaerus an hour ago | parent [-] | | Sorry but what have I said wrong? The nature of code written in kernel development is such that using unsafe is inevitable. Low-level code with memory juggling and patterns that you usually don't find in application code. And yes, I have had a look into the examples - maybe one or two years there was a significant patch submitted to the kernel and number of unsafe sections made me realize at that moment that Rust, in terms of kernel development, might not be what it is advertised for. > https://git.kernel.org/pub/scm/linux/kernel/git/a.hindborg/l.. Right? Thank you for the example. Let's first start by saying the obvious - this is not an upstream driver but a fork and it is also considered by its author to be a PoC at best. You can see this acknowledged by its very web page, https://rust-for-linux.com/nvme-driver, by saying "The driver is not currently suitable for general use.". So, I am not sure what point did you try to make by giving something that is not even a production quality code? Now let's move to the analysis of the code. The whole code, without crates, counts only 1500 LoC (?). Quite small but ok. Let's see the unsafe sections: rnvme.rs - 8x unsafe sections, 1x SyncUnsafeCell used for NvmeRequest::cmd (why?) nvme_mq/nvme_prp.rs - 1x unsafe section nvme_queue.rs - 6x unsafe not sections but complete traits nvme_mq.rs - 5x unsafe sections, 2x SyncUnsafeCell used, one for
IoQueueOperations::cmd second for AdminQueueOperations::cmd In total, this is 23x unsafe sections/traits over 1500LoC, for a driver that is not even a production quality driver. I don't have time but I wonder how large this number would become if all crates this driver is using were pulled in into the analysis too. Sorry, I am not buying that argument. | | |
| ▲ | pornel 41 minutes ago | parent | next [-] | | The idea behind the safe/unsafe split is to provide safe abstractions over code that has to be unsafe. The unsafe parts have to be written and verified manually very carefully, but once that's done, the compiler can ensure that all further uses of these abstractions are correct and won't cause UB. Everything in Rust becomes "unsafe" at some lower level (every string has unsafe in its implementation, the compiler itself uses unsafe code), but as long as the lower-level unsafe is correct, the higher-level code gets safety guarantees. This allows kernel maintainers to (carefully) create safe public APIs, which will be much safer to use by others. C doesn't have such explicit split, and its abstraction powers are weaker, so it doesn't let maintainer create APIs that can't cause UB even if misused. | |
| ▲ | baq 42 minutes ago | parent | prev [-] | | > I am not sure what point did you try to make by giving something that is not even a production quality code? let's start by prefacing that 'production quality' C is 100% unsafe in Rust terms. > Sorry, I am not buying that argument. here's where we fundamentally disagree: you listed a couple dozen unsafe places in 1.5kLOC of code; let's be generous and say that's 10% - and you're trying to sell it as a bad thing, whereas I'm seeing the same numbers and think it's a great improvement over status quo ante. |
|
|
|
|