为AVR串口实现core::fmt::Write时遭遇编译器Bug
AVR串口实现
core::fmt::Write时的unwrap/write_fmt异常问题 问题现象
为AVR串口监视器实现core::fmt::Write trait时出现以下异常:
- 直接调用
serial.write_str("hello world")可正常输出内容 - 调用
serial.write_str("hello world").unwrap()时,串口会输出大量类似编译器错误的异常字符串,且原消息越长,异常内容越多 - 调用
write_fmt则直接崩溃,推测该问题与unwrap触发的异常存在关联
自定义AVR目标配置
{ "arch": "avr", "cpu": "atmega328p", "data-layout": "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8-a:8", "max-atomic-width": 0, "env": "", "executables": true, "linker": "avr-gcc", "linker-flavor": "gcc", "linker-is-gnu": true, "llvm-target": "avr-unknown-unknown", "os": "unknown", "position-independent-executables": false, "exe-suffix": ".elf", "eh-frame-header": false, "pre-link-args": { "gcc": ["-mmcu=atmega328p"] }, "late-link-args": { "gcc": ["-lgcc", "-lc"] }, "target-c-int-width": "16", "target-endian": "little", "target-pointer-width": "16", "vendor": "unknown" }
unwrap触发的异常输出
args.len()C:\Users\Jett\.rustup\toolchains\nightly-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\core\src\fmt\mod.rsunsafe precondition(s) violated: slice::from_raw_parts requires the pointer to be aligned and non-null, and the total size of the slice not to exceed 'isize::MAX'called 'Option::unwrap()' on a 'None' valueC:\Users\Jett\.rustup\toolchains\nightly-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\core\src\char\convert.rsC:\Users\Jett\.rustup\toolchains\nightly-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\core\src\str\iter.rsC:\Users\Jett\.rustup\toolchains\nightly-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\core\src\str\validations.rsErrorattempt to add with overflowC:\Users\Jett\.rustup\toolchains\nightly-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\core\src\iter\traits\accum.rsunsafe precondition(s) violated: slice::from_raw_parts requires the pointer to be aligned and non-null, and the total size of the slice not to exceed 'isize::MAX'attempt to add with overflowunsafe precondition(s) violated: slice::from_raw_parts requires the pointer to be aligned and non-null, and the total size of the slice not to exceed 'isize::MAX'C:\Users\Jett\.rustup\toolchains\nightly-x86_64-pc-windows-msvc\lib\rust
Write trait实现代码
impl Write for Serial { fn write_str(&mut self, s: &str) -> core::fmt::Result { for c in s.chars() { self.transmit(c as u8); } Ok(()) } }
问题分析与解决方案
核心原因
AVR属于无操作系统的嵌入式目标,默认没有实现自定义panic handler。当unwrap()触发panic时,Rust的默认panic机制会尝试打印调试信息,而这个过程会调用core::fmt相关逻辑,意外复用了你实现的串口Write实例,导致递归调用、内存越界或数据溢出——这解释了异常内容随原消息变长而增加,以及write_fmt直接崩溃的现象。
解决步骤
实现独立的panic handler
编写不依赖core::fmt::Write的panic处理函数,直接通过串口发送原始字节,避免递归调用:#[panic_handler] fn panic(info: &core::panic::PanicInfo) -> ! { // 初始化串口或使用全局静态实例 let mut serial = Serial::new(); // 直接发送固定提示信息 serial.transmit_str("Panic occurred!\r\n"); // 手动处理panic位置信息,避免调用fmt逻辑 if let Some(location) = info.location() { // 发送文件名 serial.transmit_str("File: "); for c in location.file().chars() { serial.transmit(c as u8); } serial.transmit_str("\r\n"); // 发送行号(手动转ASCII) serial.transmit_str("Line: "); let mut line_num = location.line() as u32; let mut buf = [0u8; 5]; let mut idx = buf.len(); while line_num > 0 { idx -= 1; buf[idx] = (line_num % 10) as u8 + b'0'; line_num /= 10; } for &b in &buf[idx..] { serial.transmit(b); } serial.transmit_str("\r\n"); } // 死循环挂起设备 loop {} }避免使用
unwrap()
在嵌入式环境中,直接通过match或if let处理core::fmt::Result,不要依赖panic机制:match serial.write_str("hello world") { Ok(_) => {}, Err(_) => { /* 处理错误 */ } }检查串口
transmit方法
确保transmit是阻塞式实现,能正确处理所有ASCII字符(包括换行、回车),避免因发送不完整导致的内存异常。验证目标配置
尝试移除late-link-args中的-lc(AVR的libc可能与Rust标准库存在冲突),确认max-atomic-width设置为0符合atmega328p的硬件特性。
内容的提问来源于stack exchange,提问作者Kestrel
相关产品推荐
相关产品推荐

