
在 go 中使用全局变量时,必须注意类型匹配和赋值方式:声明为指针类型(如 `*rest.api`)后,需用 `=` 而非 `:=` 赋值,否则会意外创建同名局部变量,导致全局变量未被初始化。
go 不支持真正的“动态作用域”,所有变量作用域严格由词法决定。在你的代码中,问题根源在于两个关键错误:
- 类型不匹配:rest.NewApi() 返回的是 *rest.Api(指向 rest.Api 结构体的指针),但你声明的全局变量是 var api rest.Api(值类型)。这会导致编译失败或运行时 panic(取决于具体版本和用法);
- 变量遮蔽(Shadowing):在 foo() 函数中,api := rest.NewApi() 使用短变量声明 := 创建了一个新的局部变量 api,它完全遮蔽了同名全局变量。因此,全局 api 始终为 nil,后续调用 api.MakeHandler() 会 panic 或返回无效 handler,导致 http 路由失效。
✅ 正确做法如下:
package main import ( "github.com/ant0ine/go-json-rest/rest" "log" "net/http" ) type Message struct { Body string } var api *rest.Api // ✅ 声明为指针类型 *rest.Api func hostLookup(w rest.ResponseWriter, req *rest.Request) { ip, err := net.LookupIP(req.PathParam("host")) if err != nil { rest.Error(w, err.Error(), http.StatusInternalServerError) return } w.Writejson(&ip) } func foo() { api = rest.NewApi() // ✅ 使用 = 赋值,修改全局变量 api.Use(rest.DefaultDevStack...) router, err := rest.MakeRouter( &rest.Route{"GET", "/lookup/#host", hostLookup}, ) if err != nil { log.Fatal(err) } api.Setapp(router) } func bar() { // ✅ 此时 api 已初始化,MakeHandler() 可安全调用 log.Fatal(http.ListenAndServe(":8080", api.MakeHandler())) } func main() { foo() bar() }
⚠️ 注意事项:
- Go 中 := 仅用于声明并初始化新变量;若左侧变量已存在(如全局变量),必须显式使用 = 赋值;
- 全局变量初始化应确保在任何依赖它的操作(如 MakeHandler())之前完成,推荐在 main() 的早期阶段或专用初始化函数中完成;
- 对于 Web 框架等需要状态共享的场景,优先考虑将依赖项作为参数传递(更易测试、更符合 Go 的显式风格),全局变量仅适用于真正跨包、生命周期与程序一致的单例对象。
总结:Go 的简洁性依赖于明确的语义——:= 是声明,= 是赋值;类型必须精确匹配,尤其是指针与值的区别。修复这两点,即可让全局 api 正确承载路由逻辑并响应请求。