(no title)
SlySherZ | 4 years ago
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.
megous|4 years ago
For example, can rust make sure some kind of function is not called at any level when called from certain kind of context (say atomic context).
Can rust help detect/prevent you from accessing some multi-register SFRs or memory locations from main context without making you disable interrupts first in the main context?
And this constraint is only relevant if the SFR or said memory location is ever accessed from some ISR, and the access is not atomic, otherwise disabling interrupts is not necessary.
Do you have to wrap all these accesses and keep track of all this manually just like in C or does rust have some concept of "colored" functions, so that you're forced to wrap accesses to certain memory locations only if these accesses are shared between differently colored functions.
I'm not a rust person either. Just wondering how rust would help me with the most complicated aspects of concurrency in low level programming. Allocating/deallocating memory and keeping track of it is easy compared to this.
cesarb|4 years ago
In your case, you could implement something like an InterruptDisabler<T>, and use it to wrap an object which has the methods to access these registers. When you wanted to access these registers, you would do something like "my_interrupt_disabler.disable_interrupts()", which would disable interrupts and return an InterruptDisablerGuard<T>, which then would you to access the T. Once that InterruptDisablerGuard<T> gets out of scope, the compiler automatically calls the drop() method from the Drop trait on it, and that method could enable interrupts again. You cannot access the inner object without going through the guard, and you cannot get the guard without disabling interrupts.
And that's only one possible implementation. Another one would be to have something like an "interrupts disabled" token which is returned by a "disable interrupts" function, which is consumed (that is, destroyed) by an "enable interrupts" function, and which is !Send and !Sync (so it cannot be passed to another thread). The methods which access these registers would require you to give them a reference to the token, so they could only be called if you somehow obtained the token, which could only be done by disabling interrupts in the current thread. And for when you're within the interrupt handler itself, the low-level code which calls it could manufacture one of these tokens and pass a reference to it to the interrupt handler, so it would be able to access the registers without calling the "disable interrupts" function.
Of course, both of these ideas still require you to decide which registers are safe to call with interrupts enabled, and which registers are not, when creating the objects which represent the register blocks. Rust can help the developer, but it's not a panacea.