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

为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直接崩溃的现象。

解决步骤

  1. 实现独立的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 {}
    }
    
  2. 避免使用unwrap()
    在嵌入式环境中,直接通过match或if let处理core::fmt::Result,不要依赖panic机制:

    match serial.write_str("hello world") {
        Ok(_) => {},
        Err(_) => { /* 处理错误 */ }
    }
    
  3. 检查串口transmit方法
    确保transmit是阻塞式实现,能正确处理所有ASCII字符(包括换行、回车),避免因发送不完整导致的内存异常。

  4. 验证目标配置
    尝试移除late-link-args中的-lc(AVR的libc可能与Rust标准库存在冲突),确认max-atomic-width设置为0符合atmega328p的硬件特性。

内容的提问来源于stack exchange,提问作者Kestrel

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 00:41:14