Rust中如何获取字节数组非起始位置的&[u8]切片引用
Rust 缓冲区偏移拷贝实现方案
Rust 原生支持等价于C指针算术的安全操作,不需要依赖unsafe裸指针,这类缓冲区提取拷贝的需求有非常成熟的标准写法。
对应C指针算术的安全实现
你要传入源缓冲区指定偏移位置的片段,直接用Rust切片的范围索引即可。切片本身是带长度信息的胖指针,&buf[offset..offset+len]的效果和C里指针偏移offset个字节完全等价,而且会自动做边界检查,越界时会直接抛出错误,不会出现C语言里非法访问内存的问题。
比如你要从大缓冲区头部取两个16字节的值,调用你写的memcpy_safe时直接传对应范围的切片就行:
// 假设large_buf是你存储原始数据的大型缓冲区 let mut small_buf1 = [0u8; 16]; let mut small_buf2 = [0u8; 16]; // 取前16字节,等价于C里直接传large_buf的起始指针 memcpy_safe(&mut small_buf1, &large_buf[0..16]); // 取偏移16字节开始的16字节,等价于C里传large_buf + 16的指针 memcpy_safe(&mut small_buf2, &large_buf[16..32]);
如果你的输入来源不可信(比如网络报文、用户输入),担心长度不足导致panic,可以用切片的get方法返回Option,自己处理长度不够的分支:
if let Some(src_part) = large_buf.get(16..32) { memcpy_safe(&mut small_buf2, src_part); } else { // 处理源缓冲区长度不足的错误 }
这类拷贝需求的标准写法
你自己手写的memcpy_safe逐字节循环其实没必要,Rust标准库已经提供了经过充分优化的拷贝方法:
- 如果要求源和目标长度完全一致,直接用切片自带的
copy_from_slice即可,性能和C标准库的memcpy持平,长度不匹配时会直接panic,避免截断导致的隐蔽bug:// 直接替换自定义的memcpy_safe即可,不需要额外封装 small_buf1.copy_from_slice(&large_buf[0..16]); small_buf2.copy_from_slice(&large_buf[16..32]); - 如果你确实需要长度不匹配时自动截断的逻辑,也不用手写逐字节循环,复用标准库方法即可,比手写循环可靠性更高:
fn memcpy_safe(dest: &mut [u8], src: &[u8]) { let copy_len = dest.len().min(src.len()); if copy_len != dest.len() || copy_len != src.len() { println!("memcpy_safe found src and dest do not have the same length, truncating to {} bytes (src: {}, dest: {})", copy_len, src.len(), dest.len()); } dest[..copy_len].copy_from_slice(&src[..copy_len]); }
额外注意事项
- 日常业务代码优先用安全切片操作,不要为了省一丢丢边界检查的开销就用unsafe裸指针做偏移,release模式下边界检查的开销极低,绝大多数场景根本感知不到
- 固定长度的小缓冲区优先用数组类型(比如
[u8;16])而不是切片,配合copy_from_slice时编译器可以直接验证长度匹配,很多时候连运行时检查都能优化掉 - 如果需要频繁做缓冲区的读写偏移操作,可以用
std::io::Cursor,比自己手动维护偏移量更不容易出错
内容的提问来源于stack exchange,提问作者Dyskord
相关产品推荐
相关产品推荐

