🎯 什麼情境該想到我

當你用 reflect,**卡在「為什麼我改不動這個值」**的時候。

⚙️ 怎麼用(步驟 / 公式)

三條法則

1. 反射從介面值走到反射物件。
反射的本質就是檢視 interface 變數裡存的那組(型別, 值)配對。入口是兩個型別:

t := reflect.TypeOf(x)    // reflect.Type
v := reflect.ValueOf(x)   // reflect.Value

2. 反射從反射物件走回介面值。

i := v.Interface()   // 把型別與值資訊包回 interface{}

所以它是可逆的——這也是為什麼 fmt.Println(v.Interface()) 會印出原本的值。

3. ⭐ 要修改反射物件,這個值必須是「可設定的」(settable)。
這是最微妙的一條,也是絕大多數 reflect 卡關的原因。

為什麼改不動:你傳的是複本

var x float64 = 3.4
v := reflect.ValueOf(x)
v.SetFloat(7.1)   // ❌ panic: reflect: reflect.Value.SetFloat using unaddressable value

reflect.ValueOf(x) 拿到的是 x 的一份複本——就算能改也改不到原本的 x,所以 Go 直接禁止。

解法:傳指標,再取 Elem()

p := reflect.ValueOf(&x)   // 這是 *float64 的複本,但它指向 x
v := p.Elem()              // 解參考 → 這個才是可設定的
v.SetFloat(7.1)            // ✅ x 變成 7.1

判斷用 v.CanSet()

一個 API 慣例

為了讓 API 精簡,Value 的 getter/setter 操作的是能容納該值的最大型別:所有有號整數都用 int64。所以 v.Int() 回傳 int64SetInt 也吃 int64,要用實際型別時得自己轉換。

🧪 我實際套用的紀錄

  • 2026-08-29:(待填)

⚠️ 注意 / 什麼時候不適用

  • 能不用就不用。反射會失去編譯期型別檢查、變慢、而且讓程式難讀。Go 1.18 起有泛型,很多以前只能靠反射的抽象現在有型別安全的寫法。
  • 反射錯誤多半是執行期 panic,不是編譯錯誤——所以要有測試覆蓋。
  • 未匯出欄位不可設定,即使透過指標也一樣。
  • 三法則本身是語言層的事實,沒有過期;但周邊 API 有增補(例如 reflect.TypeFor,Go 1.22)→ Modern Go Guidelines

🔗 相關工具