You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

#![no_std]环境下如何测试core::fmt::Display trait实现结果

no_std环境下测试core::fmt::Display实现的惯用方案

不需要手动构造core::fmt::Formatter实例——Formatter的构造逻辑虽然属于编译器内部实现,但Rust核心库已经提供了完整的封装,不需要使用者直接触碰。以下两种是社区通用的合规方案:

方案1:栈固定缓冲区+core::fmt::Write(无任何堆依赖,适配所有no_std场景)

核心库自带的core::fmt::Write trait不依赖堆分配,你只需要给一个栈上固定长度的缓冲区实现该trait,就可以直接用write!宏触发Display的格式化逻辑,整个过程完全在栈上完成,适配包括裸机在内的所有no_std环境。
示例代码:

#![no_std]
use core::fmt::{Display, Formatter, Result, Write};

struct B {}

impl Display for B {
    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
        write!(f, "A {} C", "B")
    }
}

/// 栈上固定大小缓冲区,N根据待测格式化输出的最大长度调整即可
struct StackBuf<const N: usize> {
    buf: [u8; N],
    used: usize,
}

impl<const N: usize> StackBuf<N> {
    fn new() -> Self {
        Self {
            buf: [0; N],
            used: 0,
        }
    }

    /// 读取已写入的字符串内容,用于断言校验
    fn as_str(&self) -> &str {
        core::str::from_utf8(&self.buf[..self.used]).expect("写入内容包含非法UTF-8")
    }
}

impl<const N: usize> Write for StackBuf<N> {
    fn write_str(&mut self, s: &str) -> Result {
        let bytes = s.as_bytes();
        if self.used + bytes.len() > N {
            return Err(core::fmt::Error);
        }
        self.buf[self.used..self.used + bytes.len()].copy_from_slice(bytes);
        self.used += bytes.len();
        Ok(())
    }
}

#[test]
fn test_display_impl() {
    let mut buf = StackBuf::<32>::new();
    write!(buf, "{}", B {}).expect("格式化写入失败,缓冲区可能不足");
    assert_eq!(buf.as_str(), "A B C");
}

该方案的特点:

  • 零外部依赖,完全基于core库公开API实现,不涉及任何编译器内部未稳定的接口
  • 无堆分配,可直接在无内存分配器的裸机no_std环境下运行
  • 格式化逻辑和std环境下的format!走完全相同的路径,测试结果准确

方案2:测试阶段引入alloc(适合在开发宿主环境跑单元测试的场景)

大部分no_std项目只是正式运行环境不允许堆分配,单元测试通常是在x86等有完整运行时的开发机上执行,这种场景下可以通过cfg门控,仅在测试模块引入alloc库,直接使用alloc::format!,写法和std环境完全一致,且不会影响正式发布的no_std二进制。
示例代码:

#![no_std]
// 注意:alloc仅在测试编译时被链接,正式构建不会引入
#[cfg(test)]
extern crate alloc;

use core::fmt::{Display, Formatter, Result};

struct B {}

impl Display for B {
    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
        write!(f, "A {} C", "B")
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use alloc::format;

    #[test]
    fn it_works() {
        assert_eq!(format!("{}", B {}), "A B C");
    }
}

该方案的特点:

  • 测试代码写法和std环境无差异,维护成本极低
  • 有cfg门控隔离,正式no_std目标不会链接alloc,不会引入堆依赖
  • 仅适合有堆分配能力的测试运行环境,无法直接在无分配器的裸机目标上跑测试

注意:不要尝试手动构造Formatter实例,该类型的内部布局、构造方法没有任何稳定保证,随编译器版本可能发生变化,所有格式化场景都应该通过write!、writeln!等官方提供的宏完成,这些宏会自动处理Formatter的创建和生命周期管理。

内容的提问来源于stack exchange,提问作者Michał Zabielski

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 23:30:51