#![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
相关产品推荐
相关产品推荐

