binary.putvarint要求目标切片容量≥10字节且不自动扩容,传入未初始化或容量不足的[]byte会panic;必须用返回值n截取buf[:n],并手动管理写入偏移,否则导致错位或脏数据。

Go 语言里 binary.PutVarint 不能直接调用,它只接受 []byte 类型的底层缓冲区,且要求你手动管理写入位置——不是“传个变量就编码完事”的函数。
为什么 binary.PutVarint 总是 panic 或写错长度?
常见错误是传入未初始化或长度不足的切片,比如:buf := make([]byte, 0) 然后直接 binary.PutVarint(buf, x)。该函数不会扩容切片,只往已有底层数组里写,而变长整型(varint)最多占 10 字节(int64),若 buf 容量小于所需字节数,就会 panic “index out of range”。
- 必须确保目标切片容量 ≥ 10(对
int64)或 ≥ 5(对int32) - 函数返回实际写入字节数,但不更新切片长度;你需要自己用
buf[:n]截取有效部分 - 它只处理有符号整数(
int64),传uint64会按补码解释,结果非预期
binary.PutVarint 的正确调用姿势
典型用法是配合 bytes.Buffer 或预分配切片 + 手动偏移管理:
buf := make([]byte, 10) // 预分配足够空间 n := binary.PutVarint(buf, 123) encoded := buf[:n] // 必须截取,否则可能包含脏数据
更安全的做法是用 bytes.Buffer 封装:
var b bytes.Buffer binary.WriteVarint(&b, 123) // 注意:这是 bytes.Buffer 的方法,不是 binary 包的函数
但注意:binary.WriteVarint 是 bytes.Buffer 的方法,不是 binary 包导出的函数——binary 包本身只有 PutVarint 和 Varint(解码)两个函数,没有 “Write” 版本。
- 别混淆
binary.PutVarint(dst []byte, x int64)和buffer.WriteVarint(x) - 如果要用
binary.PutVarint,务必检查返回值n,并用dst[:n]提取结果 - 多次编码需维护写入偏移,例如:
off += binary.PutVarint(buf[off:], val)
和 protobuf varint 编码兼容吗?
完全兼容。Go 的 binary.PutVarint 实现的就是标准的 LEB128(Little-Endian Base 128)有符号变长编码,和 Protocol Buffers 规范一致。但要注意:
- protobuf 对无符号数用
zigzag编码(如uint32、sint32),而binary.PutVarint只处理int64,所以编码负数没问题,但编码uint64前必须转成int64(仅当值 ≤ 9223372036854775807) - 想编码大
uint64(如时间戳),得自己实现 zigzag:binary.PutVarint(buf, int64((x>63))) - 性能上,
PutVarint是纯内存操作,比proto.Marshal轻量得多,适合自定义二进制协议
真正容易被忽略的是:它不负责内存管理,也不做类型适配——传什么类型、怎么截取、如何拼接多个 varint,全靠你自己盯住 len、cap 和返回值 n。写错一个偏移,后面所有解码都会错位。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











