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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 15:09:45