8 ms·
This is because of how the tutorial author and the crate they use chose to represent the IO. In this case it is a Mutex, wrapping a reference counted pointer, w
by epilys 3y ago
This is because of how the tutorial author and the crate they use chose to represent the IO. In this case it is a Mutex, wrapping a reference counted pointer, wrapping an Option<[GPIO type]> to prevent uninitialised stuff being accessed before they are ready. So the "problem" here is the secure abstractions they use over I/O. You can write another interface to avoid the borrow().borrow().unwrap().unwrap().whatever() chain calls.
It might look better if you split it in lines:
let mutex = MY_GPIO.borrow(cs).borrow().as_ref().unwrap();
mutex.odr.modify(|_, w| w.odr1().set_bit());
- z3phyr 3y agoGod! Something about verbosity makes me nervous.
- sophacles 3y agoMy experience with that verbosity is: * its annoying to write, good tooling helps (e.g. using the rust-analyzer in an lsp enabled editor) * it's a godsend when revisiting code months/years later, and in reading unfamiliar code. Some languages have verbosity for it's own sake, but I don't really see rust as one of those - most of it actually inform the reader about what's going on without a need to go track down all sorts of info elsewhere to understand what effects any given line will have.
- capableweb 3y agoMY_GPIO.borrow(cs).borrow().as_ref().unwrap().odr.modify(|_, w| w.odr1().set_bit()); or let mutex = MY_GPIO.borrow(cs).borrow().as_ref().unwrap(); mutex.odr.modify(|_, w| w.odr1().set_bit()); Both of those ways are really horrible, and differ almost nothing. I understand and know why it looks like it does, but having to jump through all that mental gymnastics just to understand/write one line is why Rust is just overly verbose for a lot of things.
- FrustratedMonky 3y agoIs that one line more verbose than adding multiple lines of passing information through all the functions to provide all the same features? Probably not clear, but all of those functions are doing something that is helpful in additional checks. As others pointed out, you can set a pointer if you like in a simple line, but that isn't safe. The verbosity is providing the features, at least it is on one line, instead of 20 or 100 lines.
- capableweb 3y agoOf course the verbosity is doing something, the same is true for Java as well, it's not like the verbosity is just useless characters doing nothing. Still makes it overly verbose for a lot of us. I still write Rust code daily, but I wish the language thought a bit more about read/write ergonomics.
- oneshtein 3y agoIt's API, not a language. Write your own, ergonomic and safe API on top of unsafe code.
- capableweb 3y agoDon't act like this verbosity isn't widespread in the Rust ecosystem already.
- oneshtein 3y agoI'm a professional software developer. In my experience, Rust code is shorter than equivalent C or C++ code, with proper error handling, no memory leaks, no race conditions. If you rewrite the code above in a proper C, you will have a page of code at least.
- xinayder 3y ago> It might look better if you split it in lines: You and other Rust enthusiasts are missing the point. In C or any C-based language or even Python, you can just call either a function passing the reference to the GPIO pointer, or just GPIO |= 1. You can't do that in Rust, or no one has shown a much better way to set a bit in Rust without writing verbose code that has dozens of borrowings done.
- masklinn 3y agoThat’s not a point being missed. The comment you reply to lays out explicitly why all these components are here, and it’s the same sort of thinking which leads to the results of TFA. Your reply amounts to “I don’t wanna tho”.
- rgoulter 3y agoAs I understand the it: OP picked upon a sophisticated example that's multithreaded; the example shows off some fancy Rust techniques which deals with this complex case. It's 'fair' to say that OP's code snippet looks complex. But, it's not 'fair' to treat OP's snippet as if it's how one must write a simple blinky program. And it's good to explain that the code is complex because it's dealing with a complex case. I'd somewhat acknowledge the frustration, though. If you're trying to learn something and you take a wrong step or look in the wrong place, you don't have the intuition to figure out "here's what you want to be doing instead".
- arlort 3y agoUnless you're overloading the |= to death that assignment isn't checking any of the things that are being checked (either at compile or runtime) in the rust code This specific example is from the concurrency chapter so it's already handling multiple threads and that's why all those checks are needed. This is a random example I found to blink a led on a rp2040: > let mut led_pin = pins.gpio25.into_push_pull_output(); > > loop { > led_pin.set_high().unwrap(); > delay.delay_ms(500); > led_pin.set_low().unwrap(); > delay.delay_ms(500); > } And this is how to do it using embassy, which is an async framework for embedded in rust: https://github.com/embassy-rs/embassy/blob/main/examples/rp/src/bin/blinky.rs https://github.com/embassy-rs/embassy/blob/main/examples/rp/...
- cyber_kinetist 3y agoI'm curious if all the mutexes and reference counted pointers might contribute to any runtime cost, and to what extent this is relevant in the embedded world. (I don't know that much about the embedded field, but I've heard they're also pretty sensitive in terms of performance, so I'm curious.) Some people have written the equivalent C code for this (`*GPIO |= 1;`). Are the existing C embedded programmers typically not using refcounts / mutexes in their code since they know that they are upholding the invariants in their hardware and therefore do not have to employ these runtime checks, or are they just writing this one-liner out of laziness?
- mips_r4300i 3y agoEmbedded stuff roughly falls into 2 types: 1.Superloop, for simpler stuff. No worries about atomic register writes, at most you have a few interrupts. You don't need mutexes here. The overhead will always be low but it gets very hairy quickly with the more things you try to do concurrently. 2. Rtos-based. Think microkernel but with just a thread scheduler. You might have a dedicated IO thread that handles messages from your worker thread, which may listen to a GUI thread. In this case, you will need a way to prevent multiple threads from trying to use the same resource, whether that is a pin, a configuration register, an interrupt flag that is in the same register as an exception flag and must be cleared atomically via a special shadow register, etc... If you do all hardware touching through 1 thread you don't really need them, but otherwise, you absolutely need mutexes. And there can be a substantial performance cost to using them too much. As an example, say you had a UART receive thread that fired from an ISR handler each time a byte was received. You could naively grab the UART mutex and read the new data, clear the flag, and deal with any error conditions, then release it again. Even a 100mhz MCU could have trouble keeping up with a trivial 100kbaud serial stream due to the overhead of using the mutex too much. RTOSes do have other means for locking resources, but you would want to understand exactly what you're doing and minimize overhead as much as possible. I am not at all experienced in Rust but it seems that touching the hardware directly could end up being a large pain point.
- auggierose 3y agoJust admitting that this looks horrible would give me more confidence that the Rust community consists of sane people.
- epilys 3y agoIt does look horrible, never denied that in my post. Also, I am not part of any community despite writing Rust. There's a lot of stuff online that is borderline insanity in terms of developer UX. I personally try not to generalise on things that stand out like that; it's selection bias.