如何使用no_std将整数作为字符串写入字节数组?

And*_*kih 8 bare-metal rust

我想做的事情如下:

let x = 123;
let mut buf = [0 as u8; 20];
format_to!(x --> buf);
assert_eq!(&buf[..3], &b"123"[..]);
Run Code Online (Sandbox Code Playgroud)

#![no_std]没有任何内存分配器.

据我所知,有一个core::fmt::Displayfor 的实现u64,我想尽可能使用它.

换句话说,我想做类似的事情format!(...),但没有内存分配器.我怎样才能做到这一点?

She*_*ter 7

让我们从标准版开始:

use std::io::Write;

fn main() {
    let x = 123;
    let mut buf = [0 as u8; 20];
    write!(&mut buf[..], "{}", x).expect("Can't write");
    assert_eq!(&buf[0..3], b"123");
}
Run Code Online (Sandbox Code Playgroud)

如果我们删除标准库:

#![feature(lang_items)]
#![no_std]

use core::panic::PanicInfo;

#[lang = "eh_personality"]
extern "C" fn eh_personality() {}

#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
    loop {}
}

fn main() {
    let x = 123;
    let mut buf = [0 as u8; 20];
    write!(&mut buf[..], "{}", x).expect("Can't write");
    assert_eq!(&buf[0..3], b"123");
}
Run Code Online (Sandbox Code Playgroud)

我们得到了错误

error[E0599]: no method named `write_fmt` found for type `&mut [u8]` in the current scope
  --> src/main.rs:17:5
   |
17 |     write!(&mut buf[..], "{}", x).expect("Can't 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)
Run Code Online (Sandbox Code Playgroud)

write_fmt是通过核心库实现的core::fmt::Write.如果我们自己实现它,我们可以传递该错误:

#![feature(lang_items)]
#![feature(start)]
#![no_std]

use core::panic::PanicInfo;

#[lang = "eh_personality"]
extern "C" fn eh_personality() {}

#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
    loop {}
}

use core::fmt::{self, Write};

struct Wrapper<'a> {
    buf: &'a mut [u8],
    offset: usize,
}

impl<'a> Wrapper<'a> {
    fn new(buf: &'a mut [u8]) -> Self {
        Wrapper {
            buf: buf,
            offset: 0,
        }
    }
}

impl<'a> fmt::Write for Wrapper<'a> {
    fn write_str(&mut self, s: &str) -> fmt::Result {
        let bytes = s.as_bytes();

        // Skip over already-copied data
        let remainder = &mut self.buf[self.offset..];
        // Check if there is space remaining (return error instead of panicking)
        if remainder.len() < bytes.len() { return Err(core::fmt::Error); }
        // Make the two slices the same length
        let remainder = &mut remainder[..bytes.len()];
        // Copy
        remainder.copy_from_slice(bytes);

        // Update offset to avoid overwriting
        self.offset += bytes.len();

        Ok(())
    }
}

#[start]
fn start(_argc: isize, _argv: *const *const u8) -> isize {
    let x = 123;
    let mut buf = [0 as u8; 20];
    write!(Wrapper::new(&mut buf), "{}", x).expect("Can't write");
    assert_eq!(&buf[0..3], b"123");
    0
}
Run Code Online (Sandbox Code Playgroud)

请注意,我们正在复制io::Cursor此包装器的行为.通常,对a的多次写入&mut [u8]将相互覆盖.这对于重用分配很有用,但在连续写入相同数据时没有用.

那么只需要编写一个宏就可以了.