1. 程式人生 > >Go語言中的常見陷阱

Go語言中的常見陷阱

摘要:本文介紹了Go初學者很可能會遇到的三個常見陷阱。以下是譯文。

我最近開發了我的第一個真正的Go程式。它叫“Fix All Conflicts(譯者注:修復所有的衝突)”,或簡稱為fac。這是一個簡單易用的控制檯程式,用於解決git合併衝突。我之所以開發這麼個工具,是因為我一直都沒有找到一個好用的合併工具。

開發的過程非常有意思,我在這個過程中學到了很多東西。所以,我決定記錄下初學者很可能會遇到的一些常見“陷阱”!

有時候,地鼠很可能相當有侵略性。

1. Range

range函式是Go中最常用的函式之一。下面是range函式的使用示例。請注意,基於一些瘋狂的原因,我們決定讓動物園裡所有的動物都擁有999

條腿。

type Animal struct {
    name string
    legs int
}

func main() {
  zoo := []Animal{ Animal{ "Dog", 4 },
                   Animal{ "Chicken", 2 },
                   Animal{ "Snail", 0 },
                 }

  fmt.Printf("-> Before update %v\n", zoo)

  for _, animal := range zoo {
    // Oppps! `animal` is a copy of an element
animal.legs = 999 } fmt.Printf("\n-> After update %v\n", zoo) }

上面的程式碼看起來沒什麼問題。但是,你可能會驚訝地發現兩個fmt.Printf()語句打印出來的結果是相同的。

-> Before update [{Dog 4} {Chicken 2} {Snail 0}]
-> After update [{Dog 4} {Chicken 2} {Snail 0}]

教訓

range的value屬性(這裡是animal)是zoo的值的一個副本,而不是指向zoo中的值的指標

修復

要修改陣列中元素的值,我們必須通過它的指標來修改。

for idx, _ := range zoo {
  zoo[idx].legs = 999
}

這個看起來可能很平常,但你可能會驚訝地發現這是最常見的錯誤之一。

2. The … thingy

你可能會在C語言中使用關鍵字來建立變長引數函式, 變長引數函式接受數量或型別可變的引數。

在C語言中,你必須呼叫va_arg巨集來訪問可選引數。如果用其他方式來使用可變引數,編譯器就會報錯。

int add_em_up (int count,...) {
  ...
  va_start (ap, count);         /* Initialize the argument list */
  for (i = 0; i < count; i++)
      sum += va_arg(ap, int);   /* Get the next argument value */
  va_end (ap);                  /* Clean up */
  return sum
}

然而,在Go中,情況有點相似,但又有很大的不同。下面是Go中的一個可變引數函式myFprint。請注意它是如何使用可變引數a的。

func myFprint(format string, a ...interface{}) {
    if len(a) == 0 {
        fmt.Printf(format)
    } else {
        // ⚠️ `a` should be `a...`
        fmt.Printf(format, a)
        // ✅
        fmt.Printf(format, a...)
    }
}

func main() {
    myFprint("%s : line %d\n", "file.txt", 49)
}
[file.txt %!s(int=49)] : line %!d(MISSING)
file.txt : line 49

你可能會認為編譯器會因為我們錯誤地使用了可變引數a而報錯。但是,請注意,fmt.Sprintf只是用了a中的第一個引數。

教訓

在Go中,可變引數函式會被編譯器轉換成slices

這意味著可變引數a實際上只是一個slice。正因為如此,下面的程式碼是完全正確的。

// `a` is just a slice!
for _, elem := range a {
    fmt.Println(elem)
}

修復

記住,在使用可變引數的地方,請輸入三個點(…)!

3. Slicing 切片

如果你瞭解Python中的slicing的話,你應該會知道Python中的slicing其實是給了你一個新的列表,該列表中的元素是對複製過去的元素的引用。因此,Python的程式碼是這樣的。

a = [1, 2, 3]
b = a[:2]           # �� 完全是一個新的list
b[0] = 999
>>> a
[1, 2, 3]
>>> b
[999, 2]

但是,如果你在Go中編寫同樣的程式碼的話,就會遇到其他問題。

func main() {
  data := []int{1,2,3}
  slice := data[:2]
  slice[0] = 999

  fmt.Println(data)
  fmt.Println(slice)
}

教訓

在Go中,切片與原始片共享相同的陣列空間及其容量。因此,如果更改切片中的元素,也會改變原始陣列中的內容。

修復

如果你想得到一個單獨的切片,有兩個選擇。

// Option #1
// appending elements to a nil slice
// `...` changes slice to arguments for the variadic function `append`
a := append([]int{}, data[:2]...)

// Option #1
// Create slice with length of 2
// copy(dest, src)
a := make([]int, 2)
copy(a, data[:2]

1月13日,SDCC 2017之資料庫線上峰會即將強勢來襲,秉承乾貨實料(案例)的內容原則,邀請了來自阿里巴巴、騰訊、微博、網易等多家企業的資料庫專家及高校研究學者,圍繞Oracle、MySQL、PostgreSQL、Redis等熱點資料庫技術展開,從核心技術的深挖到高可用實踐的剖析,打造精華壓縮式分享,舉一反三,思辨互搏,報名及更多詳情可點選此處檢視
這裡寫圖片描述

相關推薦

Go語言常見陷阱

摘要:本文介紹了Go初學者很可能會遇到的三個常見陷阱。以下是譯文。 我最近開發了我的第一個真正的Go程式。它叫“Fix All Conflicts(譯者注:修復所有的衝突)”,或簡稱為fac。這是一個簡單易用的控制檯程式,用於解決git合併衝突。我之所以開

Go -- 在Go語言使用JSON struct

字母 ims bgcolor board channel rgs pick marshal player Encode 將一個對象編碼成JSON數據,接受一個interface{}對象,返回[]byte和error: func Marshal(v interface{}

VC++和C語言常見數據類型轉換為字符串的方法

char* 北京 相同 字符指針 ascii bst sdn sprint 出現 1。短整型(int) itoa(i,temp,10);///將i轉換為字符串放入temp中,最後一個數字表示十進制 itoa(i,temp,2); ///按二進制方式轉換 2。長整型(long

如何在GO語言使用Kubernetes API?

GO KUbernetes Kubernetes API 微服務 控制器 Rancher Labs首席軟件工程師Alena Prokharchyk受邀在2017年12月6-8日的CNCF主辦的Kubernetes領域頂級盛會KubeCon + CloudNativeCon 2017北美峰會

002_解析go語言的回調函數

println 函數類 () pos span 示例 lB 解析 作用 回調函數是一種特殊的函數寫法,在很多場景中發揮廣泛的作用。但是對於初學者來說,回調函數是比較頭疼的一個東西,不太好懂,筆者研究了一番,以網上的一個例子詳細說明一下 首先看一個代碼示例(來源於網上) pa

go語言通過http訪問需要認證的api

read light {} true 訪問 users tps ada 返回    func main() { //生成client 參數為默認 client := &http.Client{} //生成要訪問的url url := "https://a

005_針對於go語言速率限制的思考

回來 條件 chan 針對 完成 結果 int ++ if條件 在之前的go語言的速率限制這篇文章裏,我們嘗試了普通的速率限制,和脈沖型速率限制。其中,脈沖型速率限制是放開了限制,裏面有3個請求是一次性到達,然後再按照200ms的速度限制的,之前的代碼如下所示: pack

Go語言正則表達式的使用

Go語言正則表達式 Go語言正則表達式的使用 Go語言 正則表達式 許多程序語言都支持使用正則表達式對字符串進行操作,Go語言也不例外,正則表達式的語法網上很多教程,本文主要介紹在Go語言中如何使用正則表達式,通過以下實例進行說明,並添加了詳細的註釋,不會的小夥伴一看就明白了。 func ma

Go 語言數據類型的判斷

Go 語言中數據類型的判斷 Go數據類型判斷 Go數據類型 Go 語言中數據類型的判斷,本文介紹三種方法。方法一:使用i.(type)結合空接口(interface{}) func main() { v1 := "中國你好" v2 := 20 var v3 byte =

Go語言的反射機制reflect

Go語言中的反射機制reflect Go語言反射機制 Go語言reflect go的反射機制是要通過接口來進行的,而類似於Java的Object的空接口可以和任何類型進行交互,因此對基本數據類型等的反射也直接利用了這一特點。 package main import ( "fmt"

Go語言日誌處理,log包的使用

Go語言日誌 Go語言log golang日誌處理 Golang提供了原生日誌庫“log”,使用簡單方便,本文以代碼為例進行說明介紹。 package main import ( "os" "log" "fmt" ) func main() { // 打開日誌文

各種加密算法在Go語言的使用

各種加密算法在Go語言中的使用 Go語言使用加密算法 Go語言加密 使用SHA256、MD5、RIPEMD160 import ( "fmt" "crypto/sha256" "os" "io" "crypto/md5" "golang.org/x/

Go語言時間函數及定時器的使用

Go語言時間函數 Go語言定時器 Go語言中時間函數及定時器、休眠等功能的實現和使用,代碼如下,有需要的小夥伴直接拿去 package main import ( "time" "fmt" ) func main() { // 設置時區,如果name是""或"UTC",返回

Go語言的單例模式

syn main 單例模式 return and type KS int pack 單例模式 使用單例的目的是為了保證在整個系統中存在唯一的實例 package main import ( "fmt" "sync" ) type IntA

Go語言-make陷阱和閉包函數

相關 運行 closure Go 需要 ber enc seq sum Go語言make陷阱 a := make([]int, 3) a = append(a, 1, 2, 3) 切片大小變成6 匿名函數 匿名函數是指不需要定義函數名的一種函數實現方式,可以直接賦值給一

Go語言的控制語句

sel pre pytho Go語言 跳轉 sele index project sleep // code_004_process_control project main.go package main import ( "fmt" "time" )

Go語言的常量

識別 如果 集中管理 圓周率 導出 數據 變量 hat 一次 1 概述 常量,一經定義不可更改的量。功能角度看,當出現不需要被更改的數據時,應該使用常量進行存儲,例如圓周率。從語法的角度看,使用常量可以保證數據,在整個運行期間內,不會被更改。例如當前處理器的架構類型,可以保

Go語言的運算符

成語 就是 號碼 嚴格 內存地址 type 更改 能夠 存儲空間 1 概述 Go語言提供了,算術,關系,邏輯,位,指針,賦值運算符。本篇整體說明一下。 2 算術運算 + 相加 - 相減 * 相乘 / 相除 % 求余 ++ 自增 -- 自減 幾個細節,需要留意一下。 整

Go語言多字節字符的處理

語言環境 對應關系 相對 全部 special 每一個 圖片 轉換 完整 1 概述 Go語言的字符串是使用 UTF-8 編碼的。UTF-8 是 Unicode 的實現方式之一。本文內容包括:UTF-8 和 Unicode 的關系,Go語言提供的 unicode 包和 uni

Go語言的字符串處理

定義 sizeof gui bst utf ges 替換字符串 asp equal 1 概述 字符串,string,一串固定長度的字符連接起來的字符集合。Go語言的字符串是使用UTF-8編碼的。UTF-8是Unicode的實現方式之一。 Go語言原生支持字符串。使用雙引號(