
在Go中,若结构体方法使用值接收者(如 func (s MyStruct) Method()),则方法内对结构体字段的修改仅作用于参数副本,不会影响原始实例;必须改用指针接收者(func (s *MyStruct) Method())才能持久化变更。
在go中,若结构体方法使用值接收者(如 `func (s mystruct) method()`),则方法内对结构体字段的修改仅作用于参数副本,不会影响原始实例;必须改用指针接收者(`func (s *mystruct) method()`)才能持久化变更。
你遇到的问题——init() 中成功打印了 srv.server.Addr,但 start() 中该字段却为空——根本原因在于 Go 的方法接收者机制:*你为 init、start 和 startServerRoutine 方法定义的接收者均为值类型 tServer,而非指针类型 `tServer`**。
当调用 server.init() 时,Go 会将 server 的完整副本传入方法。你在 init() 内部执行 srv.server.Addr = ...,实际修改的是这个临时副本中的字段;方法返回后,该副本被销毁,原始全局变量 server 的 server.Addr 字段仍保持零值(空字符串)。同理,start() 接收的也是新副本,自然读不到之前“看似成功”的赋值。
✅ 正确做法是统一将接收者改为指针类型,确保所有方法操作同一内存地址上的结构体实例:
func (srv *tServer) init() {
srv.server.Addr = srv.ipAddress + ":" + srv.port
log.Println("srv.server.Addr =", srv.server.Addr)
}
func (srv *tServer) start() {
log.Println("srv.server.Addr =", srv.server.Addr)
go srv.startServerRoutine()
}
func (srv *tServer) startServerRoutine() {
log.Println("Server started at", srv.server.Addr)
err := srv.server.ListenAndServe()
if err != nil {
log.Println("Server Error:", err)
return
}
}同时,确保 main() 中调用方式不变(Go 会自动取地址):
立即学习“go语言免费学习笔记(深入)”;
func main() {
server.ipAddress = "0.0.0.0"
server.port = "12345"
server.init() // ✅ 自动转换为 &server.init()
server.start()
time.Sleep(time.Second * 5)
}⚠️ 注意事项:
- 值接收者适用于小型、只读或无需修改状态的结构体(如
type Point struct{ X, Y int }的func (p Point) Distance() float64); - 任何需修改结构体字段、或结构体较大(避免复制开销)的场景,必须使用指针接收者;
- 混合使用值/指针接收者会导致方法集不一致(例如
*T实现了某接口,而T未实现),可能引发接口赋值失败等隐蔽问题; - 初始化逻辑建议集中处理:可将
ipAddress、port赋值与init()合并为一个构造函数(如NewServer(ip, port string) *tServer),提升可测试性与封装性。
通过修正接收者类型,server.init() 的赋值将真实生效,后续 start() 及 startServerRoutine() 均能正确访问 server.Addr,彻底解决“赋值无效”的困惑。


















