Go 100 mistakes 之如何正確設定列舉值中的零值
本文是對《100 Go Mistackes:How to Avoid Them》一書的翻譯。因翻譯水平有限,難免存在翻譯準確性問題,敬請諒解。 本文首發於微信公眾號 “Go 學堂”,想獲取更多資訊,請關注該公眾號。
列舉型別是由一組值組成的資料型別。在 Go 語言中,沒有 enum 這樣的關鍵字。然而,處理一組值最好的方法是用型別別名和常量。但是,我們無法達到其他語言所能達到的安全水平。這就是為什麼我們在處理列舉值時必須要小心的原因。讓我們來看一些相關的實踐以及如何避免一些常見的錯誤。
下面列出了一週中周幾的列表:
type Weekday int ①
const (
Monday Weekday = 0 ②
Tuesday Weekday = 1
Wednesday Weekday = 2
Thursday Weekday = 3
Friday Weekday = 4
Saturday Weekday = 5
Sunday Weekday = 6
)
① 定義一個自定義的 Weekday 型別
② 建立一個 Weekday 型別的 Modany 常量
建立一個 Weekday 型別的好處是可以強制讓編譯時做型別檢查以及提高可讀性。如果我們沒有建立一個 Weekday 型別,那麼下面的函式簽名對於呼叫者來說可能會有一點模糊:
func GetCurrentWeekday() int {
// ...
}
一個 int 型別可以包含任何值,同時閱讀者如果沒有相關的閱讀文件或者程式碼的話也不能猜出該函式返回的是什麼值。相反,如果定義一個 Weekday 型別,那麼就會使該函式的簽名更清晰:
func getCurrentWeekday() Weekday {
// ...
}
在這個例子中,我們強制指定了返回具體的型別。
我們建立 Weekday 型別的列舉值的方法是比較合適的。然而,在 Go 中,還有有一種慣用的方法來宣告列舉中的常量,那就是使用常量生成器 iota
注意:在本例中,我們還可以將 Weekday 宣告為 uint32,以強制正值並確保每個 Weekday 變數分配 32 位。
iota
iota 用於建立一系列相關值,而無需明確設定這些值。 它指示編譯器複製每個常量表示式,直到塊結束或找到賦值。
下面是用 iota 的 Weekday 版本:
type Weekday int
const (
Monday Weekday = iota ①
Tuesday
Wednesday
Thursday
Friday
Saturday
Sunday
)
① 使用 iota 定義列舉值
itoa 的值從 0 開始並每行增加 1。此版本等同於第一個版本:
- Monday = 0
- Tuesday = 1
- Wednesday = 3
- etc
使用 iota 允許我們避免手動定義常量值。例如,在大的列舉中手動設定常量值是會容易出錯的。進一步說,我們不用對每一個變數都重複指定 Weekday 型別:我們定義的所有變數都是一個 Weekday 型別。
注意:我們可以在更復雜的表示式中使用 iota。下面是從 Effective Go 中出現的一個關於處理 ByteSize 列舉值的例子:
type ByteSize float64 const ( _ = iota ① KB ByteSize = 1 << (10 * iota) ② MB ③ GB TB PB EB ZB YB )
① 通過給 _ 賦值忽略第一行的值 ② 在該行 iota 等於 1,因此 KB 被設定成 1 << (10 * 1) ③ 在這一行,iota 等於 2,本行將會重複上一行的表示式,因此 MB 被設定成了 1 << (10 * 2)
讓我們看看在 Go 的列舉中如何處理未知值(unknown values)
Unknow 值
既然我們已經理解了在 Go 中處理列舉值的原理,讓我們考慮下下面的例子。我們將實現一個 HTTP 處理以便將 JSON 格式的請求解碼成 Request 結構體型別。該結構體將會包含一個 Weekday 型別的 Unknown 值。下面是第一版本的實現:
type Weekday int ①
const (
Monday Weekday = iota
Tuesday
Wednesday
Thursday
Friday
Saturday
Sunday
Unknown ②
)
type Request struct { ③
ID int `json:"id"`
Weekday Weekday `json:"weekday"`
}
func httpHandler(w http.REsponseWriter, r *http.Request) { ④
bytes, err != readBody(r) ⑤
if err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}
var request Request
err = json.Unmarshal(bytes, &request) ⑥
if err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}
// Use Request
}
① 重用我們定義的 Weekday 列舉值
② 定義 Unknown 常量
③ 定義一個包含 Weekday 欄位的 Request 結構體
④ 實現一個 HTTP 處理器
⑤ 讀取請求體並返回一個 [] byte
⑥ 解碼 JSON 請求體
在這個例子中,我們建立了一個 Request 結構體,該結構體從一個 JSON 請求體中解碼而來。這段程式碼非常完整有效。在例子中,我們可以接收一個 JSON 內容並正確解碼:
{
"id": 1234,
"weekday": 0
}
這裡,Weekday 欄位的值會等於 0:Monday。
現在,如果在 JSON 內容中不包含 weekday 欄位會怎麼樣呢?
{
"Id": 1235
}
解析該內容的時候將不會引起任何錯誤。然而,在 Request 結構體中的 weekday 欄位值將會被設定成一個 int 型別:0 值。因此,就像是在上次請求中的 Monday。
那我們應該如何區分請求中是傳遞的 Monday 還是沒有就沒有傳遞 weekday 欄位呢?這個問題和我們定義 Weekday 列舉的方式有關。實際上,Unknown 是列舉值的最後一個值。因此,它的值應該等於 7.
為了解決該問題,處理一個 unknown 的列舉值的最好的實踐方法是將它設定成 0(int 型別的零值)。因此,我們應該按如下方式生命 Weekday 列舉值:
type Weekday int
const (
Unknown Weekday = iota ①
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
Sunday
)
① Unknow 現在等於 0 了
如果 JSON 請求體中的 weekday 的值是空,那將會被解析成 Unknown;這就是我們所需要的。
根據經驗,列舉的未知值應該設定為列舉型別的零值。這樣,我們就可以區分出顯示值和缺失值了。
- 加微信實戰群請加微信(註明:實戰群):gocnio
相關文章
- Recoil 中預設值的正確處理
- go的github.com/prometheus如何在單測中校驗值是否正確GoGithubPrometheus
- MYSQL中給時間列設定預設值MySql
- 設定SQL標識列的值SQL
- 如何遍列 C# 列舉數的名稱與值C#
- Java列舉-通過值查詢對應的列舉Java
- Python中如何給字典設定預設值Python
- 陣列中查詢給定值陣列
- 巧用列舉來處理UI中顯示值與業務值不同的場景UI
- @RequestBody中列舉型別值不匹配報錯型別
- dedecms聯動型別裡,知道列舉值,如何轉換為列舉名型別
- 如何正確的開始用 Go 程式設計Go程式設計
- React技巧之設定input值React
- c++11 實現列舉值到列舉名的轉換C++
- 如何正確設定動態TextView的textSizeTextView
- 確定幾個SQL Server欄中的最大值SQLServer
- jQuery如何設定元素的屬性值jQuery
- 高版本jquery尤其是1.10.2的版本設定input radio設定值的最正確的姿勢。jQuery
- cxf WebService設定wsdl中soapAction的值Web
- go mistakesGo
- C/C++列舉enum分別列印輸出列舉子和列舉值的方法C++
- JavaScript 專題之如何求陣列的最大值和最小值JavaScript陣列
- mysql中sql_mode值設定MySql
- Swift列舉關聯值的記憶體探究Swift記憶體
- 對於返回值型別不確定的函式如何限定返回值型別型別函式
- 如何設定新資料庫的PGA值資料庫
- 數字之魅:尋找陣列中的最大值和最小值陣列
- Golang技巧之預設值設定的高階玩法Golang
- 如何過濾掉 PHP 陣列中的空值?PHP陣列
- js如何刪除陣列中重複的值JS陣列
- js如何獲取陣列中的最大值JS陣列
- 區塊鏈100講:TOKEN≠數字貨幣,區塊鏈開發者要如何正確理解TOKEN的價值!區塊鏈
- Go十大常見錯誤第一篇:未知列舉值Go
- Go 通過反射的reflect設定實際變數的值Go反射變數
- 去除陣列中的 null 值陣列Null
- JavaScript陣列中的最大值和最小值JavaScript陣列
- onethink支援給session設定陣列值嗎?Session陣列
- sqlserver 修改列的預設值SQLServer