struct tag 與 reflect

struct tag 是 Go 留給工具的後門。gortex 的宣告式路由就靠 reflect 讀這些 tag,把一個 struct 變成整張路由表。這篇先把地基打穩。

struct tag 怎麼讀

struct tag 是欄位後面那串 raw string,慣例是空白分隔的 key:"value"

type User struct {
    Name string `json:"name" validate:"required"`
}

編譯器不解讀它,要靠 reflect 在執行期讀:

f, _ := reflect.TypeOf(User{}).FieldByName("Name")
f.Tag.Get("json")            // "name"
v, ok := f.Tag.Lookup("xml") // "", false

GetLookup 的差別很關鍵:Get 對「沒這個 key」和「key 值為空」都回 "",分不出來;Lookup 多回一個 bool。gortex 判斷 inject:""(空值 tag)就是用 Lookup,否則會把「故意留空」誤判成「沒掛 tag」。

reflect 三件套:Type / Value / Kind

  • reflect.Type:靜態結構,有幾個欄位、欄位名、tag。
  • reflect.Value:執行期的值,可讀可寫。
  • Kind:底層分類(PtrStructString…),跟具名型別不同。type UserID int 的 Kind 是 Int
v := reflect.ValueOf(&User{})
v.Kind()         // reflect.Ptr
v.Elem().Kind()  // reflect.Struct;Elem() 解一層指標
t := v.Elem().Type()
for i := 0; i < t.NumField(); i++ {
    field := t.Field(i)
    _ = field.Tag.Get("json")
}

安全遍歷與方法簽章檢查

reflect 出錯不回 error,是直接 panic。動手前先檢查:

  • 先驗 Kind:別對非 struct 呼叫 NumField()、別對非指標呼叫 Elem()
  • 跳過 unexported 欄位:field.CanInterface()(或 StructField.IsExported()),否則取值會 panic。
  • 要呼叫方法前,先 MethodByName 拿到,再看 Method.TypeNumIn / NumOut 確認簽章,才 Call

這正是 B2 路由註冊在做的事:掃欄位找 GET / POST 方法、檢查簽章、才註冊。

reflect 的代價

reflect 慢(繞過靜態分派)、繞過編譯期型別檢查、又會在執行期 panic。原則只有一條:在啟動/設定期用,別放進 hot path。gortex 就是這樣切:啟動時用 reflect 掃路由換開發體驗,production build 改用 codegen,每條請求完全不碰 reflect(見 B2、B4)。

Effective Go 精選:blank identifier、Data

blank identifier _ 在 Effective Go 有整節(The blank identifier):吃掉用不到的回傳值、for range 只要 index、以及介面檢查:

var _ io.Writer = (*MyType)(nil) // 編譯期確認 *MyType 有實作 io.Writer,沒有就編不過

Data 那節講配置:new(T) 配一塊歸零記憶體回 *Tmake 專給 slice / map / channel。reflect.New 等價於 new,B2 自動初始化 nil handler 欄位就靠它。

重點整理

  • struct tag 是欄位後的 raw string,reflect 用 Get / Lookup 讀;Lookup 能分辨「沒掛」與「掛了空值」。
  • 三件套 Type / Value / Kind;Kind 看底層分類,跟具名型別無關。
  • reflect 會 panic:先檢查 Kind、跳過 unexported、方法先驗簽章再 call。
  • reflect 慢又不安全:只在啟動期用,請求路徑別碰。
  • struct tag + reflect 就是 B2〈struct tag 宣告式路由〉(gortex-struct-tag-routing)的地基。

延伸:本系列接著用這些觀念拆 gortex 框架,原始碼見 yshengliao/gortex


大綱 Sheng,內文 Claude 協助 · 環境 Go 1.24(當時最新 release)· 本系列為事後回填整理 · 列入 20260613 blog 翻新計劃,新漆未乾。