为什么在传递参数时 fmt::Arguments as_str 不起作用?

Why does fmt::Arguments as_str not work when arguments are passed?

我正在用 no_std 为我的 OS 制作一个 VGA 打印宏,但由于某种原因无法正常工作。我正在使用 vga crate 这样我就不必自己完成所有 VGA 代码。我有一个名为 _print:

的函数
use core::fmt::{Arguments, Write};
use vga::colors::Color16;
use vga::writers::{Graphics640x480x16, GraphicsWriter};

pub fn _print(args: Arguments) {
    unsafe {
        let mut i: usize = 0;
        for (_offset, character) in args.as_str().unwrap().chars().enumerate() {
            Mode.draw_character(CurrentTextX + i, CurrentTextY, character, CurrentTextColor);
            i += 8;
        }
    }
}

然后我有一个名为 vprint! 的宏:

#[macro_export]
macro_rules! vprint {
    ($($arg:tt)*) => {
        crate::videographicsarray::_print(format_args!($($arg)*));
    };
}

我没有使用 alloc。我看到过与此完全相同的代码,但由于某种原因我的代码不起作用。它显示我输入的文本,但如果我传递任何参数,它就会出现恐慌。我做错了什么?

您的代码会出现错误,因为如果没有参数,as_str() 只有 returns Some。所以当你立即 unwrap() 它会恐慌,例如你有参数要格式化。

Get the formatted string, if it has no arguments to be formatted.

This can be used to avoid allocations in the most trivial case.

std::fmt::Arguments::as_str() docs

您可以使用 args.to_string() 代替 args.as_str().unwrap(),将其格式化为 String。所以它实际上是格式化的,不管有没有参数。

pub fn _print(args: Arguments) {
    unsafe {
        let mut i: usize = 0;
        for (_offset, character) in args.to_string().chars().enumerate() {
            Mode.draw_character(CurrentTextX + i, CurrentTextY, character, CurrentTextColor);
            i += 8;
        }
    }
}

如果你想避免不必要的分配,那么你可以使用unwrap_or_else()并且只做to_string() 如果 as_str() returns None。在包装 Cow 时避免将 &str 转换为 String

use std::borrow::Cow;

let formatted_str = args
    .as_str()
    .map(Cow::Borrowed)
    .unwrap_or_else(|| Cow::Owned(args.to_string()));

no_std

因为您使用的是 no_std 而不是 alloc 箱子。然后你可以定义一个 u8 缓冲区来格式化。例如。使用 let buf = [u8; N];,其中 N 足以 space 用于您的格式化字符串。

之后,您可以围绕 &mut [u8] 缓冲区实现自己的包装器,该缓冲区实现 core::fmt::Write trait. You can use that wrapper along with with core::fmt::write().

您可以使用此答案中的 struct WriteTo,而不是自己实施:

pub fn _print(args: Arguments) {
    let mut buf = [0u8; 64];
    let formatted_str: &str = write_to::show(&mut buf, args).unwrap();

    unsafe {
        let mut i: usize = 0;
        for (_offset, _character) in formatted_str.chars().enumerate() {
            Mode.draw_character(CurrentTextX + i, CurrentTextY, character, CurrentTextColor);

            i += 8;
        }
    }
}