
本文介绍通过结构体嵌入(embedding)机制,让自定义类型自动继承 `bytes.reader` 的全部方法,并支持动态更换底层 `[]byte`,避免手动代理方法和重复分配。
Go 语言的结构体嵌入(embedding)是实现“组合优于继承”的核心机制之一。针对需要复用 bytes.Reader 行为、又需灵活替换底层数据的场景,直接嵌入 *bytes.Reader 是最简洁、符合 Go 惯例的方案:
type EZReader struct {
*bytes.Reader
}该写法使 EZReader 自动获得 Read, Seek, Len, Size, Reset 等所有 bytes.Reader 方法,无需手动实现任何代理函数。更重要的是,它天然支持运行时更换底层数据——只需重新赋值嵌入字段即可:
func (r *EZReader) Replace(b []byte) {
r.Reader = bytes.NewReader(b)
}
// 使用示例
r := &EZReader{bytes.NewReader([]byte("hello"))}
json.NewDecoder(r).Decode(&v) // 正常解码
r.Replace([]byte(`{"name":"world"}`)) // 替换数据
json.NewDecoder(r).Decode(&v) // 再次解码新内容⚠️ 注意事项:
- 嵌入字段 *bytes.Reader 是公开的(首字母大写),调用方可直接访问或修改,这是嵌入的固有特性,若需封装控制,应权衡是否真的需要完全隐藏;
- bytes.NewReader() 返回的是 *bytes.Reader,其内部不持有对输入切片的引用(仅记录指针与长度),因此 Replace 是安全的,不会引发意外的数据竞争或内存泄漏;
- 若需线程安全(如多 goroutine 并发调用 Replace 和 Read),应额外加锁,因 bytes.Reader 本身不是并发安全的。
总结:相比手动代理方法,嵌入方式更简洁、可维护性更高,且完全兼容 io.Reader 接口。它是 Go 中实现“可变数据源 Reader”模式的标准实践。









