
在Go中,若结构体方法使用值接收者(如 func (s MyStruct) Method()),则方法内对结构体字段的修改仅作用于参数副本,不会影响原始实例;必须改用指针接收者(func (s *MyStruct) Method())才能持久化修改。
在go中,若结构体方法使用值接收者(如 `func (s mystruct) method()`),则方法内对结构体字段的修改仅作用于参数副本,不会影响原始实例;必须改用指针接收者(`func (s *mystruct) method()`)才能持久化修改。
你遇到的问题本质上是 Go 语言中值接收者(value receiver)与指针接收者(pointer receiver)的根本区别所致。在你的代码中,init()、start() 和 startServerRoutine() 全部定义为值接收者:
func (srv tServer) init() { ... }
func (srv tServer) start() { ... }
func (srv tServer) startServerRoutine() { ... }
这意味着每次调用这些方法时,Go 都会将 server 实例按值拷贝一份传入方法。你在 init() 中执行的 srv.server.Addr = ... 修改的只是这个临时副本中的字段,方法返回后该副本即被销毁,原始全局变量 server 的 server.Addr 字段依然为空(零值)——这正是日志中第二、三行输出为空的原因。
而 main() 中直接对 server.ipAddress 和 server.port 的赋值之所以生效,是因为这是对变量本身的直接操作,不涉及方法调用和参数传递。
✅ 正确做法:将所有需修改结构体字段的方法改为指针接收者:
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 会自动解引用):
func main() {
server.ipAddress = "0.0.0.0"
server.port = "12345"
server.init() // ✅ 自动转换为 (&server).init()
server.start()
time.Sleep(time.Second * 5)
}
⚠️ 注意事项:
- 指针接收者方法可读可写原结构体;值接收者方法只能安全读取(除非显式返回新实例);
- 若结构体较大(如含大数组、切片或 map),值接收者还会带来不必要的内存拷贝开销;
- 同一类型的方法应保持接收者一致性:若已有某个方法使用指针接收者(例如因需修改字段),其余相关方法也建议统一使用指针接收者,避免混淆;
-
http.Server本身要求Addr字段在ListenAndServe()前已设置,否则行为未定义(通常表现为监听:80等默认端口,引发权限错误,如你日志末尾所示)。
总结:Go 中“方法能否修改接收者”完全由接收者类型决定,而非方法名或调用时机。牢记 “要修改,请用 *T;只读可选 T,但推荐统一用 *T 提升一致性与效率”。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











