不能直接用os.WriteFile写切片字节,因为[]int等切片是含指针、长度、容量的运行时结构体,强制转[]byte非法且写入的是内存地址快照,重启后失效,导致乱码或panic;必须经gob、json等序列化后再落盘。

直接用 os.WriteFile 写切片字节是错的——它只写原始内存布局,不序列化结构,读出来就是乱码或 panic。 正确做法必须经过序列化(json、gob 等)再落盘,否则数据无法还原。
为什么不能直接 os.WriteFile("data.bin", []byte(mySlice), 0644)
Go 的 []int 或 []string 是运行时管理的结构体(含指针、长度、容量),[]byte(mySlice) 这种强制转换在绝大多数情况下非法(编译报错),即使绕过(如用 unsafe),写入的也只是当前内存地址快照,重启后地址失效,读取必然崩溃或返回垃圾值。常见错误现象包括:panic: runtime error: makeslice: cap out of range 或解包后切片长度为 0。
- 切片不是纯数据,而是“描述+指向”,直接 dump 指针毫无意义
- 跨平台、跨架构、跨 Go 版本都不兼容
- 哪怕同一次运行中,append 后底层数组重分配,旧地址就失效了
encoding/gob 是最省心的原生方案(仅限 Go 内部使用)
gob 专为 Go 类型设计,保留类型信息、支持嵌套结构、性能好、体积小。但要求 encode/decode 两端类型定义完全一致(包括包路径),不适合跨语言或长期存档。
- 写入:用
gob.NewEncoder(file).Encode(data),data必须是导出变量(首字母大写)或已注册的类型 - 读取:先
file, _ := os.Open("data.gob"),再gob.NewDecoder(file).Decode(&dst),dst必须是地址且类型匹配 - 注意:
gob不支持 struct tag,字段名大小写必须和定义完全一致 - 示例中若定义
type Config struct { Port int },就不能用Port int `json:"port"`去混用
encoding/json 更通用,但要注意零值语义丢失
JSON 适合调试、跨语言、人眼可读,但会丢掉 Go 特有语义:比如 nil []string 和 []string{} 都序列化成 [],反序列化后全变成空切片;"" 字符串和 nil 字符串字段也无法区分。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
立即学习“go语言免费学习笔记(深入)”;
- 结构体字段必须首字母大写(导出),否则 JSON 输出为空对象
{} - 要用小写 key?加 tag:
Text string `json:"text"` - 写入前建议检查
err := json.Unmarshal(jsonData, &dst)是否为nil,避免静默失败 - 读文件时若
os.ReadFile返回os.ErrNotExist,应初始化默认值,而不是让dst保持nil导致后续appendpanic
必须做原子写入,否则崩溃时大概率损坏文件
直接 os.WriteFile 覆盖原文件是危险操作:写到一半进程挂了,原文件就毁了。正确流程是「写临时文件 → file.Sync() 强刷磁盘 → os.Rename() 原子替换」。
- 临时文件名建议用
filepath.Join(os.TempDir(), "data.json."+uuid.NewString())或拼.tmp后缀 - 务必调用
tmpFile.Sync(),否则数据还在 OS 缓存里,断电即丢 -
os.Rename()在同一文件系统上是原子的,失败时保留原文件,临时文件需手动清理 - 别省略错误检查:任何一步
err != nil都要处理(比如删临时文件、记录日志)
最容易被忽略的是:持久化不是「写一次就完事」,而是「每次变更都走完整原子流程 + 启动时可靠加载」。少一个环节,重启后数据就可能为空、错乱或 panic。

















