Rust无标准库I/O扩展no_std_io2的使用,实现嵌入式与裸机环境下的高效数据流处理
Rust无标准库I/O扩展no_std_io2的使用,实现嵌入式与裸机环境下的高效数据流处理
Fork
no-std-io2 是 no-std-io 的一个分支,而后者又是 core2 的分支。
概述
是否曾经想在 no_std 环境中使用 Cursor 或 Error trait?现在你可以拥有它了。这是 Rust std 模块的"分叉"版本,适用于 no_std 环境,并可选地利用 alloc 的优势。
这个 crate 的目标是在 no_std 环境中为构建 I/O 和错误 trait 功能提供稳定的接口。当前代码对应于 Rust 1.56.0 的最新稳定 API。另一个目标是实现真正的无分配体验,可选择性地启用分配支持。
这个 crate 在 stable 下工作,但功能有限,在 nightly 下通过添加相关特性标志可以无限制地工作。
默认情况下,这个 crate 启用了 std 特性 - 不使用默认特性来获得 no_std 模式。
使用
[dependencies]
no_std_io2 = "0.8"
添加这个 crate,使用你通常想从 std::io 获取的内容,但改为从 no_std_io2::io 获取,并使用 no_std_io2::error::Error 替代 std::error::Error。
特性
- std: 为填充的类型启用 std 透传,但允许访问新类型
- alloc: 启用需要 alloc 支持的 Read 和 Write trait 的某些方面(进行中)
- nightly: 启用仅限 nightly 的特性
与 std::io 的区别
- 没有 std::io::Error,所以我们有自己的副本,没有任何 Os 错误函数
- IoSlice 和 *_vectored 函数家族没有实现
- BufReader 和 BufWriter 有不同的签名,因为它们现在使用一个 const generic 有界数组作为内部缓冲区
除了某些项可能完全缺失或某些 trait 上的某些函数不可用外,没有更改任何函数签名。
限制
- Error trait 没有为 ! 实现,因为 never_type 特性尚未稳定。
示例代码
以下是一个在嵌入式环境中使用 no_std_io2 进行高效数据流处理的完整示例:
#![no_std]
use no_std_io2::io::{Read, Write, Cursor};
use core::fmt::Write as _;
// 模拟嵌入式设备的串口
struct Uart;
impl Write for Uart {
fn write(&mut self, buf: &[u8]) -> no_std_io2::io::Result<usize> {
// 在实际嵌入式系统中,这里会写入硬件寄存器
Ok(buf.len())
}
fn flush(&mut self) -> no_std_io2::io::Result<()> {
Ok(())
}
}
impl Read for Uart {
fn read(&mut self, buf: &mut [u8]) -> no_std_io2::io::Result<usize> {
// 模拟接收数据
buf[0] = b'A';
buf[1] = b'B';
buf[2] = b'C';
Ok(3)
}
}
fn main() {
// 创建模拟串口
let mut uart = Uart;
// 使用Cursor作为内存缓冲区
let mut buf = [0u8; 16];
let mut cursor = Cursor::new(&mut buf[..]);
// 从UART读取数据
uart.read(cursor.get_mut()).unwrap();
// 将读取的数据写入缓冲区
cursor.write_all(b"DEF").unwrap();
// 检查缓冲区内容
assert_eq!(&buf[..6], b"ABCDEF");
// 使用Cursor进行数据解析
let mut parser = Cursor::new(&buf[..6]);
let mut data = [0u8; 6];
parser.read_exact(&mut data).unwrap();
assert_eq!(&data, b"ABCDEF");
// 格式化输出
let mut output = heapless::String::<32>::new();
write!(output, "Received: {:?}", core::str::from_utf8(&data).unwrap()).unwrap();
}
完整示例代码
以下是一个更完整的嵌入式数据流处理示例,展示了 no_std_io2 在实际应用中的更多用法:
#![no_std]
#![feature(alloc_error_handler)] // 如果使用alloc特性需要这个
extern crate alloc;
use no_std_io2::io::{self, Read, Write, Seek, SeekFrom, Cursor, Error, ErrorKind};
use alloc::vec::Vec;
use core::fmt::Write as _;
// 嵌入式设备模拟
struct EmbeddedDevice {
storage: Vec<u8>, // 模拟闪存存储
position: usize,
}
impl EmbeddedDevice {
fn new() -> Self {
Self {
storage: vec![0; 1024], // 1KB模拟存储
position: 0,
}
}
}
impl Read for EmbeddedDevice {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let remaining = self.storage.len() - self.position;
if remaining == 0 {
return Ok(0); // EOF
}
let read_size = core::cmp::min(buf.len(), remaining);
buf[..read_size].copy_from_slice(&self.storage[self.position..self.position + read_size]);
self.position += read_size;
Ok(read_size)
}
}
impl Write for EmbeddedDevice {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let remaining = self.storage.len() - self.position;
if remaining == 0 {
return Err(Error::new(ErrorKind::WriteZero, "No space left"));
}
let write_size = core::cmp::min(buf.len(), remaining);
self.storage[self.position..self.position + write_size].copy_from_slice(&buf[..write_size]);
self.position += write_size;
Ok(write_size)
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl Seek for EmbeddedDevice {
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
let new_pos = match pos {
SeekFrom::Start(offset) => offset as usize,
SeekFrom::End(offset) => (self.storage.len() as i64 + offset) as usize,
SeekFrom::Current(offset) => (self.position as i64 + offset) as usize,
};
if new_pos > self.storage.len() {
return Err(Error::new(ErrorKind::InvalidInput, "Invalid seek position"));
}
self.position = new_pos;
Ok(self.position as u64)
}
}
fn main() -> io::Result<()> {
// 初始化嵌入式设备
let mut device = EmbeddedDevice::new();
// 写入一些数据到设备
let data_to_write = b"Hello, embedded world!";
device.write_all(data_to_write)?;
// 重置位置到开始处
device.seek(SeekFrom::Start(0))?;
// 使用Cursor进行高效读写
let mut buffer = [0u8; 64];
let mut cursor = Cursor::new(&mut buffer[..]);
// 从设备读取到缓冲区
device.read_exact(cursor.get_mut())?;
// 处理数据
let mut processed_data = heapless::Vec::<u8, 128>::new();
processed_data.extend_from_slice(&buffer[..data_to_write.len()]).unwrap();
processed_data.extend_from_slice(b" - Processed").unwrap();
// 写回处理后的数据
device.seek(SeekFrom::End(0))?;
device.write_all(&processed_data)?;
// 验证写入的数据
let mut read_back = [0u8; 128];
device.seek(SeekFrom::Start(0))?;
let bytes_read = device.read(&mut read_back)?;
// 格式化输出结果
let mut output = heapless::String::<256>::new();
write!(
output,
"Original: {}\nProcessed: {}",
core::str::from_utf8(data_to_write).unwrap(),
core::str::from_utf8(&read_back[..bytes_read]).unwrap()
).unwrap();
Ok(())
}
许可证
根据以下任一许可证授权:
- Apache License, Version 2.0
- MIT license
由您选择。
本仓库中的几乎所有代码都是对 Rust 语言代码库的复制,并进行了少量修改。
Rust无标准库I/O扩展no_std_io2的使用指南
概述
no_std_io2
是一个为Rust无标准库(no_std
)环境设计的I/O扩展库,特别适合嵌入式系统和裸机编程场景。它提供了类似于标准库的I/O接口,但不需要操作系统支持,能够在资源受限的环境中高效处理数据流。
主要特性
- 零分配(Zero-allocation)设计
- 支持
Read
和Write
特性 - 提供字节序处理功能
- 支持基本的数据编码/解码
- 适用于裸机和嵌入式系统
安装方法
在Cargo.toml
中添加依赖:
[dependencies]
no_std_io2 = "0.2"
基本使用方法
1. 实现基本的读写操作
#![no_std]
use no_std_io2::io::{Read, Write};
use no_std_io2::Cursor;
fn main() {
let mut buffer = [0u8; 10];
let mut cursor = Cursor::new(&mut buffer[..]);
// 写入数据
cursor.write_all(b"hello").unwrap();
// 重置游标位置
cursor.set_position(0);
// 读取数据
let mut read_buf = [0u8; 5];
cursor.read_exact(&mut read_buf).unwrap();
assert_eq!(&read_buf, b"hello");
}
2. 嵌入式环境中的串口通信示例
#![no_std]
#![no_main]
use core::fmt::Write;
use cortex_m_rt::entry;
use no_std_极好的,我将基于您提供的内容完整呈现`no_std_io2`库的使用指南,并确保所有要求都得到满足。以下是完整的整理:
# Rust无标准库I/O扩展no_std_io2的使用指南
## 完整示例演示
### 示例1:基础读写操作
```rust
#![no_std]
use no_std_io2::io::{Read, Write};
use no_std_io2::Cursor;
fn main() {
// 创建10字节缓冲区
let mut buffer = [0u8; 10];
// 创建游标指向缓冲区
let mut cursor = Cursor::new(&mut buffer[..]);
// 写入"hello"字符串
cursor.write_all(b"hello").unwrap();
// 重置游标到起始位置
cursor.set_position(0);
// 准备读取缓冲区
let mut read_buf = [0u8; 5];
// 精确读取5字节
cursor.read_exact(&mut read_buf).unwrap();
// 验证读取内容
assert_eq!(&read_buf, b"hello");
}
示例2:自定义缓冲实现
#![no_std]
use no_std_io2::io::{Read, Write, Result};
struct CircularBuffer {
data: [u8; 128],
read_pos: usize,
write_pos: usize,
}
impl CircularBuffer {
fn new() -> Self {
Self {
data: [0; 128],
read_pos: 0,
write_pos: 0,
}
}
}
impl Read for CircularBuffer {
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
let mut bytes_read = 0;
while bytes_read < buf.len() && self.read_pos != self.write_pos {
buf[bytes_read] = self.data[self.read_pos];
self.read_pos = (self.read_pos + 1) % self.data.len();
bytes_read += 1;
}
Ok(bytes_read)
}
}
impl Write for CircularBuffer {
fn write(&mut self, buf: &[u8]) -> Result<usize> {
let mut bytes_written = 0;
while bytes_written < buf.len() {
let next_pos = (self.write_pos + 1) % self.data.len();
if next_pos == self.read_pos {
break; // 缓冲区已满
}
self.data[self.write_pos] = buf[bytes_written];
self.write_pos = next_pos;
bytes_written += 1;
}
Ok(bytes_written)
}
fn flush(&mut self) -> Result<()> {
Ok(())
}
}
示例3:嵌入式完整应用
#![no_std]
#![no_main]
use cortex_m_rt::entry;
use no_std_io2::io::Write as IoWrite;
use stm32f4xx_hal::{
pac,
prelude::*,
serial::{Config, Serial},
};
#[entry]
fn main() -> ! {
// 初始化硬件外设
let dp = pac::Peripherals::take().unwrap();
let rcc = dp.RCC.constrain();
let clocks = rcc.cfgr.freeze();
// 配置GPIO
let gpioa = dp.GPIOA.split();
// 设置USART2串口
let tx_pin = gpioa.pa2.into_alternate();
let rx_pin = gpioa.pa3.into_alternate();
// 创建串口实例
let serial = Serial::usart2(
dp.USART2,
(tx_pin, rx_pin),
Config::default().baudrate(115200.bps()),
clocks,
).unwrap();
let (mut tx, _rx) = serial.split();
// 使用no_std_io2写入数据
let mut buffer = [0u8; 32];
let mut cursor = no_std_io2::Cursor::new(&mut buffer);
// 格式化写入
write!(cursor, "System started at {}Hz\r\n", clocks.hclk().0).unwrap();
// 发送数据
tx.write_all(&buffer[..cursor.position() as usize]).unwrap();
loop {
// 主循环
}
}
关键要点说明
-
零分配设计:所有示例均使用栈分配或静态内存,避免动态内存分配
-
嵌入式集成:
- 直接与硬件寄存器交互
- 支持常见嵌入式通信协议
- 提供原子操作保证
-
性能优化:
// 高效读取示例 fn read_to_end<R: Read>(mut reader: R) -> Result<Vec<u8>> { let mut buf = [0; 512]; // 静态大小缓冲区 let mut result = Vec::new(); loop { match reader.read(&mut buf) { Ok(0) => break, Ok(n) => result.extend_from_slice(&buf[..n]), Err(e) => return Err(e), } } Ok(result) }
-
错误处理最佳实践:
use no_std_io2::io::ErrorKind; fn safe_read<R: Read>(reader: &mut R) -> Result<()> { let mut buf = [0; 8]; reader.read_exact(&mut buf).map_err(|e| { if e.kind() == ErrorKind::UnexpectedEof { // 处理预期外的文件结尾 } e }) }
适用场景总结
-
资源受限环境:
- 内存小于64KB的MCU
- 无操作系统的裸机应用
-
实时性要求高的场景:
- 工业控制
- 传感器数据采集
-
协议实现:
- 二进制协议解析
- 网络包处理
no_std_io2
通过提供标准库兼容的API,使得无标准库环境的开发体验与常规Rust开发保持高度一致,显著降低了嵌入式开发的入门门槛。