Go cmp.Or: เลือกค่าตัวแรกที่ไม่เป็น zero value แบบอ่านรู้เรื่อง
เวลาโปรแกรมมีค่าจากหลายชั้น เช่น flag, environment, ไฟล์ config และค่าเริ่มต้น เรามักต้องเลือก ค่าตัวแรกที่ใช้งานได้ ถ้าเขียน if ไล่ทีละชั้น โค้ดไม่ผิดแต่ความตั้งใจถูกกลบด้วยพิธีการครับ ตั้งแต่ Go 1.22 ฟังก์ชัน cmp.Or บอกกติกานี้ตรง ๆ: คืนค่าตัวแรกที่ไม่ใช่ zero value หรือคืน zero value หากทุกตัวว่าง
WHY: ให้ลำดับ fallback มองเห็นในบรรทัดเดียว
ลองนึกถึงร้านข้าวตามสั่งที่ถาม “เอาเมนูจากใบสั่ง ถ้าไม่มีใช้เมนูประจำโต๊ะ ถ้ายังไม่มีเอากะเพรา” ลำดับสำคัญกว่ากลไกการถาม cmp.Or ทำให้คนอ่านเห็นลำดับนั้นทันที และ generic ของมันทำงานกับชนิดที่ comparable ได้ เช่น string, number, bool, pointer และ struct ที่เปรียบเทียบได้
package main
import (
"cmp"
"fmt"
)
type Limits struct {
Retries int
Timeout int
}
func main() {
name := cmp.Or("", "จากไฟล์", "ค่าเริ่มต้น")
retries := cmp.Or(0, 3, 5)
limits := cmp.Or(Limits{}, Limits{Retries: 3, Timeout: 10})
if name != "จากไฟล์" || retries != 3 {
panic("fallback order is wrong")
}
if limits.Retries != 3 || limits.Timeout != 10 {
panic("struct fallback is wrong")
}
fmt.Println(name, retries, limits)
}
คำว่า zero value ไม่ได้แปลว่า “ข้อมูลเสีย” เสมอไป: ค่าว่าง, ศูนย์, false และ nil อาจเป็นค่าที่ผู้ใช้ตั้งใจ หากศูนย์หมายถึง “ไม่ retry” การส่งค่าศูนย์ตามด้วยสามจะเปลี่ยนเจตนาโดยเงียบ ๆ กรณีนั้นควรใช้ pointer, คู่ค่า value/ok หรือชนิด option เพื่อแยก “ไม่ได้ตั้ง” ออกจาก “ตั้งเป็นศูนย์”
เลือกใช้เมื่อกติกาคือ first non-zero จริง ๆ
cmp.Or เหมาะกับ fallback ที่ทุกค่ามีชนิดเดียวกันและ zero value หมายถึง “ข้าม” อย่างชัดเจน ไม่เหมาะกับ validation, การแปลงชนิด หรือ fallback ที่ต้อง log เหตุผล เพราะ argument ทุกตัวถูกประเมินก่อนเรียกฟังก์ชัน ถ้าตัวเลือกหลังมีงานแพงหรือ side effect ให้กลับไปใช้ if เพื่อทำแบบ lazy
Recap
- เรียงค่าจากสำคัญที่สุดไป fallback สุดท้าย
- ยืนยันว่า zero value แปลว่า “ไม่ได้ตั้ง” จริง
- อย่าใส่งานแพงหรือ side effect ใน argument
- ใช้ pointer หรือคู่ value/ok เมื่อศูนย์เป็นค่าที่ถูกต้อง