
在 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 正确承载路由逻辑并响应请求。










