6 ms·
I'm not a Rust programmer, so be gentle with me. What worries me is that when it comes to safety, surely the chain is as strong as the weakest link? You declar
by blippage 5y ago
I'm not a Rust programmer, so be gentle with me.
What worries me is that when it comes to safety, surely the chain is as strong as the weakest link? You declare one thing unsafe, and then suddenly all bets are off. And maybe you don't even know that what you're doing is unsafe.
But here's an example I pulled from github, which blinks a Raspberry Pi Pico:
// Set GPIO25 to be an output (output enable is set)
p.SIO.gpio_oe_set.write(|w| unsafe {
w.bits(1 << 25);
w
});
Right, but what if you're fiddling with registers that might require atomic operations? Lines such as this seem to be saying "ignore all that, write to the register anyway."
Fearless concurrency, you say: are you sure? How do you know?
Suppose you have an ISR (interrupt service routine) that wants to toggle a GPIO pin. From whence does it borrow the privilege to set the pin?
Surely, at the end of the day, microcontrollers can be characterised as large state machines with global state. That's what they ARE. It's their inherent nature, which you cannot abstract away without it leaking somewhere (at least to do anything useful, anyway).
At least with C/C++ you KNOW you've got a problem, and have to reason about the problem carefully. With Rust, you THINK you've solved the problem, but have you?
"Change My View", as they say on Reddit.
*Update 1* OK, I'm going to press my point on this. I'm looking at the Embedded Rust Book (please remember, I'm not a Rust programmer) at
https://docs.rust-embedded.org/book/concurrency/index.html https://docs.rust-embedded.org/book/concurrency/index.html
I'm considering the issue of interrupts here.
" Instead, our interrupt handlers might be called at any time and must know how to access whatever shared memory we are using. At the lowest level, this means we must have statically allocated mutable memory, which both the interrupt handler and the main code can refer to."
"In Rust, such static mut variables are always unsafe to read or write, because without taking special care, you might trigger a race condition, where your access to the variable is interrupted halfway through by an interrupt which also accesses that variable."
RIGHT, but that means that Rust gives me no better assurances than C/C++. Here's their code:
static mut COUNTER: u32 = 0;
#[entry]
fn main() -> ! {
set_timer_1hz();
let mut last_state = false;
loop {
let state = read_signal_level();
if state && !last_state {
// DANGER - Not actually safe! Could cause data races.
unsafe { COUNTER += 1 };
}
last_state = state;
}
}
#[interrupt]
fn timer() {
unsafe { COUNTER = 0; }
}
Then is says "Can you spot the race condition? The increment on COUNTER is not guaranteed to be atomic"
It then solves the problem by showing how to introduce a critical section.
So at the end of the day it's just C with different syntax, and a lot more hoopla. You've made everything mutable and unsafe. Exactly the kind of thing you were trying to avoid in the first place.
- woodruffw 5y ago> At least with C/C++ you KNOW you've got a problem, and have to reason about the problem carefully. With Rust, you THINK you've solved the problem, but have you? Having written C and C++ for about a decade and Rust for about 2 years now, I don't think this is accurate: people don't reason any more carefully about C and C++ than other languages, and neither language (even in their safer subsets) encourages methodical safety thinking. This is in direct contrast to Rust's absolute requirement that you annotate anything that isn't already safe by construction with an `unsafe` keyword. You might be wrong about the safety of whatever you're constructing, but that alone is a significantly more methodical and careful approach to encapsulating the "weak" parts of programs.
- conradludgate 5y agoI think most rust programmers are aware enough to know that it's not actually 100% memory safe. But it's memory safe-r. I don't actually know anyone who's done even a little bit of rust actually believe it's 100% safe The stdlib is full of unsafe, I'd much rather they handle the unsafe parts which have been audited and battle tested and I use their abstractions. It's almost impossible to avoid unsafe somewhere in the chain. But that's not really unexpected. What's nice is that once you have a safe wrapper, it's impossible to misuse and cause memory errors
- SlySherZ 5y agoAlright, I'll try. Last time you used an higher level interpreted language (like JS), did you have to worry whether or not the machine instructions it was generating were the right ones? Generating instructions at runtime sounds pretty unsafe, no? You didn't, right? You can just mostly assume the language works and get on with your life. That's how unsafe feels to me when I'm programming in Rust, I never have to worry about it and pretty much never need to use it. Sometimes you'll run into a problem where you need to use unsafe, but that only happens very very rarely, for example when creating an interface to a C library, but most people will never need that. When that happens, sure, you'll have to be extra careful and make sure everything works. But after you're done with that small piece, you're back into safe Rust. So, how does it work in practice, if you really need to use unsafe? Suppose you have a C enum with values MyEnum {A, B, C} (but it can be any int in C!), and you want to use it in Rust. You can make an unsafe wrapper that tries to parse the enum, and it can either return None - if it was an invalid integer, or return Some(A), Some(B), Some(C). But once you get the wrapper right (which is not hard), Rust will guarantee that values of type MyEnum have values A, B or C and nothing else, so you never need to worry about the exceptional case again. You worry about it once at the boundary and that's it.