4

I'm using Rust for an embedded environment where we don't have an OS and we are building are target as no_std.

We have implemented a special pathway for the device to log error messages in case of panic -- I'm trying to implement a panic_handler lang item.

Our initial version (working) looks like this:

#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
    let mut buf = String::new();
    write!(&mut buf, "{}", info).unwrap();
    unsafe { report_panic_message(buf.as_ptr(), buf.len()) };
    unsafe { abort() }
}

Here we are using alloc::string::String to act as the buffer into which we format the panic message.

However, this implementation is bad because allocating the string could fail, potentially leading to a panic, and then the panic handler will likely fail.

Instead, we would like to format into a fixed-size buffer on the stack, and drop the dependency on alloc crate.

The attempted fix is like this:

// Maximum supported message length
const MAX_MSG_SIZE: usize = 4096;

#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
    let mut buf = [0u8; MAX_MSG_SIZE];

    match write!(&mut buf, "{}", info) {
        Ok(n) => {
            unsafe { report_panic_message(buf.as_ptr(), n) };
        }
        Err(_) => {
            let err_msg = "panic: The panic message exceeded MAX_MSG_SIZE bytes";
            unsafe { report_panic_message(err_msg.as_ptr(), err_msg.len()) };
        }
    };

    unsafe { abort() }
}

However this fails with an error message:

error[E0599]: no method named `write_fmt` found for type `&mut [u8; 4096]` in the current scope
  --> /src/lib.rs:36:11
   |
36 |     match write!(& mut buf, "{}", info) {
   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: the method `write_fmt` exists but the following trait bounds were not satisfied:
           `&mut [u8; 4096] : core::fmt::Write`
   = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)

It appears that the trait std::io::Write is implemented for &mut[u8], c.f. but it isn't implemented for core::fmt::Write. I cannot implement core::fmt::Write for a built-in type, due to the orphan rules.

What is the most idiomatic way to do this?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Chris Beck
  • 15,614
  • 4
  • 51
  • 87

0 Answers0