9 ms·
Great post, especially for the embedded crowd. I'm looking to start using Rust in FW development but hit a couple pain points. * First, bit field descriptors i
by kettro 6y ago
Great post, especially for the embedded crowd. I'm looking to start using Rust in FW development but hit a couple pain points.
* First, bit field descriptors is a huge pain - where, in C, you would define an enum for the task, there isn't a real equal in vanilla Rust, other than a litany of constant variables.
* Second, the over-reliance on nightly is a tough sell for - having inline asm gated makes it very hard.
* Third, while format is great, it's also very heavyweight. Not having (AFAIK) variadic arguments is hard, and there isn't really an equal to printf.
I would love to be proven wrong, because I have an OS to write, and would love to use Rust.
- masklinn 6y ago> First, bit field descriptors is a huge pain - where, in C, you would define an enum for the task, there isn't a real equal in vanilla Rust “A litany of constants” is exactly what your c enum is though. And you can define a "degenerate" Rust enum with explicit discriminant values: enum Foo { A = 1, B = 2, C = 4 } They are cast-able to integrals too: println!("{}", Foo::C as usize); // prints "4" Doesn’t work so well for flags however. > Second, the over-reliance on nightly is a tough sell for - having inline asm gated makes it very hard. The alternative would be to simply not make the feature available at all until it is done and ready, with even higher risk (as it would have been exercised even less). As is, if the feature is fine for you as-is you can use it, and if you can't or don't want to use nightly you can go and assist its shepherding to stable (https://github.com/rust-lang/rust/issues/72016 https://github.com/rust-lang/rust/issues/72016) > while format is great, it's also very heavyweight. Not having (AFAIK) variadic arguments is hard, and there isn't really an equal to printf. There are no safe Rust-level variadics, but Rust can call C variadic functions, and under RFC 2137 (not stable yet I think) define them as well.
- nicoburns 6y agoYou can assign numeric values to enum variants in Rust too like so: enum Field { Foo = 0b11111111, Bar = 0b10101010, } fn main() { println!("{}", Field::Foo as u8); println!("{}", Field::Bar as u8); } There is also the bitflags crate: https://docs.rs/bitflags/1.2.1/bitflags/ https://docs.rs/bitflags/1.2.1/bitflags/ For lighter weight formatting there is https://github.com/japaric/ufmt https://github.com/japaric/ufmt
- alkonaut 6y ago> You can assign numeric values to enum variants in Rust too Is this (C-style enums) new? I swear I looked for it a year or two back, found questions about it, with the answers "use constants".
- ChrisSD 6y agoThese aren't really C style enums. These are still sum types. C-style enums are sugar for defining an integer type alias and a bunch of constants without creating any new types or scope. Rust enums, even with an assigned value, define a new type that can only be one of the defined values. So this type is not an integer and cannot have a bit pattern that isn't explicitly defined.
- steveklabnik 6y agoRust calls this feature "C style enums," so while you're right there are differences, it's sort of a term of art in this context. ("style" is supposed to reflect that it's similar, but not the same thing.)
- ChrisSD 6y agoYeah. I hate it ;). I already have enough trouble trying to explain to new people the difference between C and Rust enums. The phrase "C style" just confuses things more, IMHO. Admittedly this may be a failure of communication on my part.
- steveklabnik 6y agoThat's fair :) I don't think the name was super carefully considered, to be honest, it just kinda happened.
- drran 6y agoC enums in Rust can be declared as: enum Foo { A=0, B=1, C=3, _UNKNOWN(i32), } However, this cannot be compiled by current Rust compiler.
- simias 6y ago>First, bit field descriptors is a huge pain - where, in C, you would define an enum for the task, there isn't a real equal in vanilla Rust, other than a litany of constant variables. Have a look at the bitlags crate, I use it a lot for this purpose: https://docs.rs/bitflags/1.2.1/bitflags/ https://docs.rs/bitflags/1.2.1/bitflags/ You still have to give the literal value of the field instead of using the auto-incrementing enum but that's how I do it in C as well, I find it too error-prone otherwise. And how do you deal with fields that take up multiple bits anyway? >Second, the over-reliance on nightly is a tough sell for - having inline asm gated makes it very hard. I agree 100%. I can understand them prioritizing higher level system programing and taking the time to do asm right, but it does make it a bit of a pain to do bare metal Rust at the moment. >Third, while format is great, it's also very heavyweight. Not having (AFAIK) variadic arguments is hard, and there isn't really an equal to printf. What do you have in mind here? format! is strongly typed and a lot more flexible than printf. You can't even print custom structs with printf... Actually even printing standard types like uint32_t and friends require macro soup to work portably. Variadic arguments are pretty much a no-go for a strongly typed, static language since you would need a lot of trickery to get it to work safely under the hood (hence format! being a macro). But what use case do you have for printf that can't be implemented with println!/format! and friends?
- masklinn 6y ago> But what use case do you have for printf that can't be implemented with println!/format! and friends? I'm guessing they mean that the format! machinery is rather complex and brings in a lot of code. That it's also somewhat slow is a recurring concern . Have none of the embedded folks created a specialised version of format! which is not generic, and basically only supports the types and styles which c's printf supports? edit: there's ufmt though it seems somewhat inactive and I've no idea how good it is: https://github.com/japaric/ufmt https://github.com/japaric/ufmt > Optimized for binary size and speed (rather than for compilation time) > No dynamic dispatch in generated code
- rcxdude 6y agoThere's defmt which I think i think is even better (it implements basically the same thing as I have used in a commercial C++ embedded project for a few years, and was sorely missing any open source equivilent when I was first trying to implement it). formatting strings on an embedded device is pointless and wasteful. Heck, formatting log lines before they are displayed to the user is actively harmful to interpreting them! Defmt basically gives embedded projects structured logging and a substantial bandwidth, cpu, and space overhead gain. https://ferrous-systems.com/blog/defmt/ https://ferrous-systems.com/blog/defmt/
- tambourine_man 6y ago> because I have an OS to write That sounds awesome. Personal project or commissioned?
- dochtman 6y agoHave a look at the recent defmt project announced by the Knurling-rs group: https://defmt.ferrous-systems.com/ https://defmt.ferrous-systems.com/ (And consider sponsoring them if this is of value to you: https://ferrous-systems.com/blog/knurling-one-month/ https://ferrous-systems.com/blog/knurling-one-month/.)
- JoshTriplett 6y ago> * First, bit field descriptors is a huge pain - where, in C, you would define an enum for the task, there isn't a real equal in vanilla Rust, other than a litany of constant variables. With my Rust language team hat on: we'd love to have native support for bitfields. I'd invite folks who are interested in that to propose a language MCP ("major change proposal"), which I'd expect to be approved, and then collaborate on a spec for them.
- djmips 6y agoJosh are you able to answer my top-level comment about fixed point numbers?
- JoshTriplett 6y agoSure, done.
- Ericson2314 6y agoI probably said this many places before, but I rather leapfrog C and get {u,i}N for all n and #[repr(bitpacked)].
- JoshTriplett 6y agoThere are two separate problems that'd need solving to handle bitfields: 1) Having uN and possibly iN types, for general-purpose use. 2) Placing such types at specific offsets within a struct. C only handles the second of those: you can have an N-bit type, but when you extract it it comes out as the wider type you constrain it from. So, `unsigned field:5;` will get read as an `unsigned`, not a `u5`. Rust could handle both, if appropriate sized types exist. (Const generics might be nice here, to parameterize on the type width.) (1) alone won't solve the problem, without some way to define how those types are packed into the struct.