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

无标准库环境(如单片机)下向切片写入数据的优雅实现方案

问题描述

假设有如下结构体:

struct A {
    a: u8,
    b: u16,
    c: u32
}

需要实现以下方法将其内容写入字节切片:

impl A {
    pub fn to_buffer(&self, output: &mut [u8]) -> Result<usize, Error> {
        todo!()
    }
}

代码运行在单片机环境,无法使用标准库及部分特性。当前实现方式如下:

pub fn to_buffer(&self, output: &mut [u8]) -> Result<usize, Error> {
    output[0] = self.a;
    output[1..3].copy_from_slice(self.b.to_le_bytes());
    output[3..7].copy_from_slice(self.c.to_le_bytes());
    Ok(7)
}

该实现易出错,且在切片空间不足时会触发panic。期望实现类似如下的链式调用写法:

output.write(self.a)?; 
output.write(self.b)?;
output.write(self.c)?;

可以通过Cursor实现但希望不依赖标准库,同时需要返回写入的字节数(因输出缓冲区可能是预先分配的ROM大缓冲区)。

解决方案

1. 定义自定义错误类型

先适配单片机环境定义简单的错误枚举:

#[derive(Debug, Clone, Copy)]
pub enum Error {
    BufferTooSmall,
}

2. 实现自定义切片写入器

这个结构体持有可变切片引用和当前写入偏移量,自动跟踪写入位置,避免手动计算索引:

pub struct SliceWriter<'a> {
    buf: &'a mut [u8],
    offset: usize,
}

impl<'a> SliceWriter<'a> {
    // 从可变切片初始化写入器
    pub fn new(buf: &'a mut [u8]) -> Self {
        SliceWriter { buf, offset: 0 }
    }

    // 获取已写入的字节数
    pub fn written(&self) -> usize {
        self.offset
    }
}

3. 为数值类型实现写入方法

为SliceWriter实现针对不同无符号整数的小端写入逻辑,统一处理缓冲区空间检查:

impl<'a> SliceWriter<'a> {
    // 写入u8
    pub fn write_u8(&mut self, value: u8) -> Result<(), Error> {
        if self.offset >= self.buf.len() {
            return Err(Error::BufferTooSmall);
        }
        self.buf[self.offset] = value;
        self.offset += 1;
        Ok(())
    }

    // 写入小端u16
    pub fn write_u16_le(&mut self, value: u16) -> Result<(), Error> {
        let bytes = value.to_le_bytes();
        if self.offset + bytes.len() > self.buf.len() {
            return Err(Error::BufferTooSmall);
        }
        self.buf[self.offset..self.offset + bytes.len()].copy_from_slice(&bytes);
        self.offset += bytes.len();
        Ok(())
    }

    // 写入小端u32
    pub fn write_u32_le(&mut self, value: u32) -> Result<(), Error> {
        let bytes = value.to_le_bytes();
        if self.offset + bytes.len() > self.buf.len() {
            return Err(Error::BufferTooSmall);
        }
        self.buf[self.offset..self.offset + bytes.len()].copy_from_slice(&bytes);
        self.offset += bytes.len();
        Ok(())
    }
}

如果想要统一使用write方法,可以定义一个Writable trait来抽象写入行为:

pub trait Writable {
    fn write_to(&self, writer: &mut SliceWriter) -> Result<(), Error>;
}

impl Writable for u8 {
    fn write_to(&self, writer: &mut SliceWriter) -> Result<(), Error> {
        writer.write_u8(*self)
    }
}

impl Writable for u16 {
    fn write_to(&self, writer: &mut SliceWriter) -> Result<(), Error> {
        writer.write_u16_le(*self)
    }
}

impl Writable for u32 {
    fn write_to(&self, writer: &mut SliceWriter) -> Result<(), Error> {
        writer.write_u32_le(*self)
    }
}

impl<'a> SliceWriter<'a> {
    pub fn write<T: Writable>(&mut self, value: T) -> Result<(), Error> {
        value.write_to(self)
    }
}

4. 重构A的to_buffer方法

现在可以用期望的链式调用写法实现,自动处理边界检查,最后返回写入字节数:

impl A {
    pub fn to_buffer(&self, output: &mut [u8]) -> Result<usize, Error> {
        let mut writer = SliceWriter::new(output);
        writer.write(self.a)?;
        writer.write(self.b)?;
        writer.write(self.c)?;
        Ok(writer.written())
    }
}

方案优势

  • 低错误率:无需手动计算索引和切片范围,所有边界检查由写入器统一处理,避免越界panic
  • 可读性高:链式调用写法简洁直观,符合代码预期
  • 无标准库依赖:所有逻辑自定义实现,适配单片机受限环境
  • 精确返回写入长度:通过written()方法可获取准确的已写入字节数,即使缓冲区提前耗尽也能灵活调整逻辑

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 21:34:54