It looks like the support for much of the std::panic functionality is omitted from the core::panic making it virtually useless and even harmful for many cases.
Harmful: When one opts to take the panic-free approach (as in no-panic or no-panics-whatsoever crates). In such a case when we have some library functionality which is not written with this consideration, we have no way of catching the panic, as there is no catch_unwind, or similar implemented in core.
Useless:The only macro provided for panicking is panic!() which is only taking a string payload, unlike in std which provides panic_any! capable of taking arbitrary type payloads. Needless to say, that in many cases we might rather want to pass around some non-string diagnostic information, which isn't possible currently.
I read through existing issues tracking, and it seems most of the related topics are either dated, or do not even consider it a problem. So I wonder, is this something not that "demanded" by the embedded community or there are some approaches to work around the problem?
** Note: I am talking about stable Rust. I am aware that some of the std-like functionality currently present as intrinsics
Maybe because it isn't supported
Personally, our product (and it's a major one, currently considering migrating to Rust) does not have any textual user or debug interface and runtime diagnostics can be only reported using numerical codes.
.. plus, I forgot to list the fact that panic isn't recoverable.
You shouldn't recover from panic in the current execution of the program.
In embedded the right thing to do would be to have a panic handler that logs the panic (in some way that it persists across reset) then resets the microcontroller. At next startup you can then send the logged error to whatever log handling system you may have (e.g. over UART, MQTT or something else).
In a development context it may instead make sense to break into a debugger over JTAG or similar (instead of resetting).
That's exactly what I am talking about. If I am using a library or a crate or a library which is written in a way that can panic instead of gracefully returning a Result, I want to be able to catch it and handle in a way that I see fit and not that crate designer (that's the case for the aforementioned catch_unwind).
A panic in Rust should be used for non-recoverable errors, if it isn't then look for alternative APIs (such as fallible versions of allocations if that is something you want to handle) or for another crate.
Your response on a panic should be the same as when the hardware watchdog fires. From what I work on that would be to make sure all outputs are turned off, the emergency stop circuit is activated (which will activate the brakes in hardware, bypassing software entirely) and then reset. After that send the error over CAN to a central vehicle management unit.
I do feel you on the string issue though, they take up space. One option would be to define your own macro that logs a number and then panics with an empty string. See also https://defmt.ferrous-systems.com/ (which provides numeric logging and numeric panics).
I agree that hardware should have some monitor for handling failures and software lock-ups, but I don't see why embedded code wouldn't in principle at least have the same option of catching unwinds to isolate failures at a more granular level than the whole device failing hard.
In the big-fat-std world web servers have a very good reason to use catch_unwind to isolate bugs to individual requests, so that a single bug triggered by one request doesn't interrupt the whole server and all the other work it performs. Embedded software that processes some messages and executes different tasks could similarly have use for a more granular isolation of bugs.
I agree that having non-string panics would be useful. The current panic code is old, convoluted, and IMHO needlesly tied to Box<dyn Any> and fmt::Arguments.
Of some note is that given a panic_any payload of some ZST type, the payload Box doesn't actually allocate anything. This functionally gives access to using the token types' TypeId as a numeric panic code, although of course these are explicitly unstable and not inspectable except via TypeId comparison.
It should be possible to create TypeId resolution maps as part of debuginfo associated with the build, though, for building a defmt like system from them.
There isn't any nice way for core to define a panic_any function which requires the payload to be zero sized, but it could theoretically provide a macro that takes the name of a unit struct and both does an item-const[1] ZST assertion[2] and then shoves &Token as &dyn Any + Send into a Box<dyn Any + Send> shaped ABI.
It's critical that the check is const _: () = assert!($crate::mem::size_of::<$Ty>() == 0); and not just const { assert!(…) }; (inline-const), as the latter is post-mono but the former has a strong effort to always be evaluated pre-mono. ↩︎
Alternative: abuse the transmute size check to get the same functionality with a different error message. ↩︎
Actually, looking into the plumbing that currently exists, no additional tricks should be necessary. And the ZST pseudoalloc tricks aren't necessary just for panic_impl either. It could be as simple as
although IIUC, panic_impl doesn't actually look at PanicInfo::payload currently, only PanicInfo::message. So either that'd need to get adjusted somehow or core would need to get access to rust_panic_with_hook. (Maybe a None message with &&'static dyn Any + Send /*+ZST*/ can be translated into the payload? Kinda wants to use some of the dyn* work, though…)