
println() 是底层调试函数,直接输出值的内存地址;而 fmt.Println() 才能按类型语义格式化输出(如显示 map[string]interface{} 的实际键值),这是导致 res["username"] 显示为两个内存地址的根本原因。
`println()` 是底层调试函数,直接输出值的内存地址;而 `fmt.println()` 才能按类型语义格式化输出(如显示 `map[string]interface{}` 的实际键值),这是导致 `res["username"]` 显示为两个内存地址的根本原因。
在 Go 中,println() 是一个内置的、非标准的、仅用于底层调试的函数,它不执行任何类型感知的格式化——对任意复合类型(如 map、struct、slice)都直接打印其底层指针地址或运行时表示,而非人类可读的内容。这正是你看到 (0x2b3c20, 0xc420075420) 这类双地址输出的原因:Go 运行时将 map 表示为一个包含哈希表头指针和数据指针的结构体,println 原样输出了这两个字段。
相反,fmt.Println() 属于标准库的格式化包,它通过反射机制识别值的类型,并调用对应的 String() 方法(若实现)或内置规则进行语义化打印。对于 jwt.MapClaims(本质是 map[string]interface{}),fmt 包会递归展开键值对,清晰显示 "username": "adam" 等内容。
✅ 正确做法:始终使用 fmt 包进行生产级日志与调试输出:
package main
import (
"fmt"
"github.com/dgrijalva/jwt-go"
)
func getToken() jwt.MapClaims {
tokenString := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkYW0iLCJwYXNzd29yZCI6InRlc3QiLCJpYXQiOjE0ODcyMDY2OTIsImV4cCI6MTUxODc2NDI5Mn0.6LQo_gRwXiFBvNIJOwtf9UuxoQMZZ3XNILTnU-46-Zg"
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
return []byte("supersecretkittysecret"), nil
})
if err != nil {
fmt.Printf("JWT parsing error: %v\n", err)
return nil
}
if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
return claims
}
return nil
}
func main() {
res := getToken()
if res == nil {
fmt.Println("Failed to extract claims")
return
}
// ✅ 正确:语义化输出整个 map
fmt.Println("All claims:", res)
// ✅ 正确:安全访问并打印单个字段(注意类型断言)
if username, ok := res["username"].(string); ok {
fmt.Println("Username:", username) // 输出: Username: adam
} else {
fmt.Println("username is not a string or missing")
}
// ❌ 错误:避免使用 println(无格式化、不可移植、不推荐)
// println(res["username"]) // → 输出类似 (0x..., 0x...) 的地址
}
⚠️ 注意事项:
-
jwt.MapClaims是map[string]interface{}的别名,其中值可能是string、float64(JSON number)、bool或嵌套map,必须做类型断言才能安全使用(如res["username"].(string)),否则运行时 panic; - 不要返回
*jwt.MapClaims(即*map[string]interface{}):既无必要(map 本身是引用类型),又增加复杂度且易引发空指针问题; -
println已被 Go 官方明确标记为“for debugging only”,禁止在生产代码中使用;应统一采用fmt包(fmt.Println,fmt.Printf,log等)。
总结:问题本质不是指针返回或解引用错误,而是输出函数选择不当。切换到 fmt.Println 并辅以正确的类型断言,即可获得预期的可读结果。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











