Uber Go 規範 -二-: Slice、Struct、Map
Uber 是一家美國硅谷的科技公司,也是 Go 語言的早期 adopter。其開源了很多 golang 項目,諸如被 Gopher 圈熟知的 zap、jaeger 等。2018 年年末 Uber 將內部的 Go 風格規範 開源到 GitHub,經過一年的積累和更新,該規範已經初具規模,並受到廣大 Gopher 的關注,本文是對規範的整理
https://github.com/xxjwxc/uber_go_guide_cn
- 參數和返回 (slice & map)
slice
和 map
包含了指向底層數據的指針,因此當在函數中使用他們作爲參數和返回值時,需要特別注意。
1.1 作爲參數
a. 反面示例
type User struct {
Likes []string
}
func (u *User) SetLikes(likes []string) {
u.Likes = likes
}
func TestRun(t *testing.T) {
like := []string{"籃球", "旅遊", "聽歌"}
user := new(User)
// 切片作爲參數傳遞
user.SetLikes(like)
fmt.Println("user Like1: ", user.Likes)
// 這裏修改變量,會影響到user對象中的屬性
like[1] = "學習"
fmt.Println("user Like2 : ", user.Likes)
}
/**輸出
=== RUN TestRun
user Like1: [籃球 旅遊 聽歌]
user Like2 : [籃球 學習 聽歌]
--- PASS: TestRun (0.00s)
PASS
*/
b. 推薦寫法
// 只需要修改這個方法,其他不變
func (u *User) SetLikes(likes []string) {
u.Likes = make([]string, len(likes))
// 這裏使用了複製
copy(u.Likes, likes)
}
/**輸出
=== RUN TestRun
user Like1: [籃球 旅遊 聽歌]
user Like2 : [籃球 旅遊 聽歌]
--- PASS: TestRun (0.00s)
PASS
*/
1. 2 作爲返回值
a. 反面示例
type Stats struct {
mu sync.Mutex
counters map[string]int
}
// Snapshot 返回當前狀態。
func (s *Stats) Snapshot() map[string]int {
s.mu.Lock()
defer s.mu.Unlock()
// 返回的對象中的屬性
return s.counters
}
// snapshot 不再受互斥鎖保護
// 因此對 snapshot 的任何訪問都將受到數據競爭的影響
// 影響 stats.counters
snapshot := stats.Snapshot()
b. 推薦寫法
type Stats struct {
mu sync.Mutex
counters map[string]int
}
func (s *Stats) Snapshot() map[string]int {
s.mu.Lock()
defer s.mu.Unlock()
// 這裏重新定義一個變量,複製結果返回
result := make(map[string]int, len(s.counters))
for k, v := range s.counters {
result[k] = v
}
return result
}
// snapshot 現在是一個拷貝
snapshot := stats.Snapshot()
- 結構體嵌入
嵌入的類型,應放在結構體內字段列表的頂部,並且必須有一個空行將嵌入式字段與常規字段分隔開。
2.1 反面示例
type Client struct {
version int
http.Client
}
2.2 推薦寫法
// 1.嵌入類型在頂部
// 2.空行將嵌入式字段與常規字段分隔開
type Client struct {
http.Client
version int
}
- 初始化結構體
3.1 使用字段名初始化
// 反面示例
k := User{"John", "Doe", true}
// 推薦寫法
k := User{
FirstName: "John",
LastName: "Doe",
Admin: true,
}
3.2 對零值結構使用 var
如果在聲明中省略了結構的所有字段,請使用 var
聲明結構。
// 反面示例
user := User{}
// 推薦
var user User
3.3 初始化Struct
引用
在初始化結構引用時,請使用&T{}
代替new(T)
,以使其與結構體初始化一致。
// 反面示例
sval := T{Name: "foo"}
// inconsistent
sptr := new(T)
sptr.Name = "bar"
// 推薦
sval := T{Name: "foo"}
sptr := &T{Name: "bar"}
- nil 是一個有效的 slice
4.1 當返回長度爲零的切片時
// 反面示例
if x == "" {
return []int{}
}
// 推薦寫法
if x == "" {
return nil
}
4.2 檢查切片是否爲空
要檢查切片是否爲空,請始終使用
len(s) == 0
。而非nil
。
// 反面示例
func isEmpty(s []string) bool {
return s == nil
}
// 推薦示例
func isEmpty(s []string) bool {
return len(s) == 0
}
4.3 零值切片
零值切片(用var
聲明的切片)可立即使用,無需調用make()
創建。
a. 反面示例
nums := []int{}
// or, nums := make([]int)
if add1 {
nums = append(nums, 1)
}
if add2 {
nums = append(nums, 2)
}
b. 推薦寫法
var nums []int
if add1 {
nums = append(nums, 1)
}
if add2 {
nums = append(nums, 2)
}
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/IXwBhZu7C5jB10Nx5_vGPg