应选gob或encoding/binary取决于需求:gob适用于Go进程间临时通信,要求结构体字段导出、指针非nil、类型完全一致;encoding/binary适用于跨语言、固定布局场景,需手动处理字段顺序、长度前缀、字节序及变长字段。

gob 和 encoding/binary 是 Go 里最常被拿来序列化二进制文件的两个包,但它们根本不是一回事——选错就卡在第一行代码。
你真正要问的是:该用哪个?怎么写才不 panic、不丢字段、不跨平台错乱?
gob.Encode 一调就 panic: "nil pointer dereference"
这不是结构体为 nil 的问题,而是 gob.NewEncoder 的参数 w io.Writer 为空,或者结构体里有导出的指针字段(比如 User *UserInfo)但值是 nil。
-
w必须是非nil的可写对象:用bytes.Buffer{}或已建立连接的net.Conn,别直接传nil或未初始化的变量 - 导出指针字段必须非
nil:哪怕只是临时赋个零值结构体,比如&UserInfo{},否则gob会尝试解引用崩溃 -
json:",omitempty"标签完全无效:gob忽略所有 struct tag,别指望靠它跳过字段 - 私有字段(小写开头)会被静默跳过:不是报错,而是根本没编码进去,容易误判成“数据丢了”
binary.Write 写完文件多出几百个零字节
因为 binary.Write 按类型大小完整写入,比如字段是 [1024]byte,哪怕只填了前 5 字节,后面 1019 个零也会被写进去。它不识别 slice、map、指针或 interface{},也不支持变长字段。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
立即学习“go语言免费学习笔记(深入)”;
- 别把大数组塞进 struct 后直接
binary.Write:改用[]byte字段 + 显式长度前缀(如uint16)+ 手动分步写入 - struct 必须全是固定大小类型(
int32、[8]byte),不能含[]byte、map、指针等 - 字段顺序就是协议顺序:改一个字段位置,旧数据就 decode 失败;字段名、大小写、包路径全得一致
- 字节序必须显式指定:
binary.LittleEndian和binary.BigEndian不能混用,网络协议通常用前者,文件格式常见后者
Decode 后字段全为零,还不报错
gob 和 binary.Read 都不会主动告诉你“类型不匹配”,而是静默失败或提前返回 io.ErrUnexpectedEOF。尤其是 gob,类型路径不同(main.User ≠ otherpkg.User)就彻底失效。
-
gob要求两端结构体定义完全一致:包路径、字段名大小写、字段顺序、是否导出,缺一不可 - 新增字段只能加在末尾,且类型要向后兼容(
string→string可以,int→int64不行) -
binary.Read读取前务必用io.ReadFull:它保证读满指定字节数,避免因网络/文件截断导致后续解析偏移错乱 - 测试时别依赖真实文件:用
bytes.NewReader([]byte{...})硬编码十六进制样本,比反复改文件快得多
TCP 上直接套 gob,等于裸奔
gob 本身不带消息边界,Decode() 会卡住、粘包、半包,甚至返回 io.ErrUnexpectedEOF 却不告诉你哪条消息坏了。decoder 实例也不能复用。
- 必须自己加帧头:比如前 4 字节写消息长度(
uint32),再写 payload;解码端先读长度,再按长度读完整块 - 每次 decode 前新建
gob.Decoder:复用 decoder 在粘包场景下极易状态错乱 - 别传
map[string]interface{}或第三方类型:它们没有运行时类型信息,decode 出来基本是空 map 或 panic;应定义明确结构体,或提前用gob.Register(&MyType{})注册
真正麻烦的从来不是函数怎么调,而是协议定义和内存布局之间那层看不见的 gap —— 字段顺序、对齐、符号位、长度前缀,每个都可能让两个看似正常的 Encode/Read 调用彻底失联。

















