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

  1. 參數和返回 (slice & map)

slicemap 包含了指向底層數據的指針,因此當在函數中使用他們作爲參數和返回值時,需要特別注意。

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()
  1. 結構體嵌入

嵌入的類型,應放在結構體內字段列表的頂部,並且必須有一個空行將嵌入式字段與常規字段分隔開。

2.1 反面示例

type Client struct {
  version int
  http.Client
}

2.2 推薦寫法

// 1.嵌入類型在頂部 
// 2.空行將嵌入式字段與常規字段分隔開
type Client struct {
  http.Client

  version int
}
  1. 初始化結構體

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"}
  1. nil 是一個有效的 slice

4.1  當返回長度爲零的切片時

// 反面示例
if x == "" {
  return []int{}
}

// 推薦寫法
if x == "" {
  return nil
}

4.2 檢查切片是否爲空

要檢查切片是否爲空,請始終使用len(s) == 0。而非 nil

// 反面示例
func isEmpty([]string) bool {
  return s == nil
}

// 推薦示例
func isEmpty([]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