bincode序列化结构体写文件时size_of不符导致read_exact失败
问题根源
std::mem::size_of返回的是类型在栈内存中的布局大小,和bincode序列化后的字节长度没有关联:
- 你的
Node结构包含String类型,64位系统下String在栈上存储3个8字节字段:堆数据指针、字符串长度、容量,共24字节,加上end_ptr的4字节u32,经内存对齐后总大小为32字节。这个值仅表示栈上存储结构体元数据的空间,和字符串实际内容长度无关。 - bincode序列化时不会写入内存中的指针值,默认对
String的序列化逻辑为:先写入8字节u64类型的长度前缀,再写入字符串的实际二进制内容,最后写入其他字段。示例中name为"node_1"共6字节,加8字节长度前缀、4字节end_ptr,总长度为8+6+4=18字节,和文件实际写入长度完全匹配。
另外原代码还有两个隐藏问题:
- 调用
file.write不保证写入全部缓冲区内容,该方法可能返回短写的字节数,需要用write_all保证完整写入。 - 写入完成后文件游标停在文件末尾,直接调用
read_exact会从末尾开始读取,即使缓冲区长度正确也会读失败,需要先将游标移动到对应读取位置。
正确实现方案
方案1:直接获取序列化结果的长度
调用bincode::serialize返回的缓冲区本身的len()就是序列化后的准确字节数,直接使用即可:
use std::fs::{File, OpenOptions}; use std::io::prelude::*; use bincode::*; use serde::{Serialize, Deserialize}; #[derive(Serialize, Deserialize, PartialEq, Debug)] pub struct Node { pub name: String, end_ptr: u32 } fn main() { let node = Node { name: String::from("node_1"), end_ptr: 0 }; let node_as_buf = bincode::serialize(&node).unwrap(); let serialized_len = node_as_buf.len(); // 直接拿到准确的序列化长度 let mut file = OpenOptions::new() .read(true) .write(true) .create(true) .truncate(true) .open("test.txt") .unwrap(); file.write_all(&node_as_buf).unwrap(); // 保证完整写入所有字节 file.seek(std::io::SeekFrom::Start(0)).unwrap(); // 将游标移回读取起始位置 let mut buffer = vec![0u8; serialized_len]; file.read_exact(&mut buffer).unwrap(); let read_node: Node = bincode::deserialize(&buffer).unwrap(); assert_eq!(node, read_node); }
方案2:预计算序列化长度(不生成序列化缓冲区)
如果需要提前拿到序列化长度做文件指针偏移、预分配空间,不需要先执行序列化,可以直接调用bincode提供的serialized_size方法:
let serialized_len = bincode::serialized_size(&node).unwrap() as usize;
该方法会遍历结构体字段计算序列化后的总长度,结果和实际序列化后的长度完全一致,没有额外的缓冲区开销。
方案3:固定长度序列化适配文件指针场景
如果后续要基于文件偏移做指针跳转,希望序列化长度可控,可以根据需求调整bincode配置,或替换变长字段:
- 若允许字段内容变长,可开启固定整数编码配置,让长度前缀等元数据占用固定字节数:
let config = bincode::config::standard().with_fixed_int_encoding(); let (node_as_buf, written_len) = bincode::serde::encode_to_vec(&node, config).unwrap(); - 若需要完全固定的序列化长度,不随字段内容变化,需要替换
String、Vec这类变长类型为固定长度数组,例如用[u8; 16]存储固定长度名称,此时序列化后的长度可以在编译期确定,完全可以满足文件内固定偏移跳转的需求。
内容的提问来源于stack exchange,提问作者m.tracey
相关产品推荐
相关产品推荐

