學過C,python,或者Java之類語言再來學習golang應該是無壓力,看看文法就能寫。文法上比較特殊的如下:
聲明變數並賦值使用 :=
a, b := 1, 2 //聲明變數a,b,並且賦值1,2
a = 2 //a賦值2
if 不需要圓括弧,並且可以執行運算式.for語句類似
if x:=1; x<2 {
}
String()函數<同java的toString(),對象的字串形式>中如果有對自身的"%s"操作,將導致無窮遞迴調用。因為"%s"會調用String(),將對象轉化為字串形式。
type MyString string
func (m MyString) String() string {
return fmt.Sprintf("MyString=%s", m) // Error: will recur forever. return fmt.Sprintf("MyString=%s", string(m)) // OK: note conversion.
}
string(m)將m轉化為一個String對象,然後“%s”是作用在該String對象上,不是m本身,所以不回導致無窮遞迴
defer有點finally的味道,但是又有不同
for i := 0; i < 5; i++ { defer fmt.Printf("%d ", i)}Deferred functions are executed in LIFO後進先出<棧> order, so this code will cause 4 3 2 1 0
func trace(s string) string { fmt.Println("entering:", s) return s}func un(s string) { fmt.Println("leaving:", s)}func a() { defer un(trace("a")) fmt.Println("in a")}func b() { defer un(trace("b")) fmt.Println("in b") a()}func main() { b()}The arguments to the deferred function (which include the receiver if the function is a method) areevaluated when the defer executes, not when the call executes.
defer語句執行時,立即計算被defer的函數所需要的所有參數,而不是等到被defer的函數執行才計算該函數的參數。結果如下
entering: bin bentering: ain aleaving: aleaving: b
new and make
new(T) returns a pointer to a newly allocated zero value of type T。new適合初始化0值對象,new(File) and &File{} are equivalent。new([]int) returns a pointer to a newly allocated, zeroed slice structure, that is, a pointer to a nil slice value.
make(T, args) creates slices, maps, and channels only, and it returns an initialized (not zeroed) value of type T (not *T)
Arrays
Arrays are values. Assigning one array to another copies all the elements. 數組不是引用,是值,數組a賦值給b,直接拷貝成另一份b,結果是數組a,b獨立,內容相同
In particular, if you pass an array to a function, it will receive a copy of the array, not a pointer to it. 數組作為參數,傳遞的也是整個數組的值的拷貝,而不是指標
以上兩點與C語言不同,每次拷貝數組代價昂貴,如果希望類似C語言,可以傳遞指標。
Slices
Slices行為才與C的數組一致,是指標傳遞。Slices更常用。Slices底層是數組,只要不超過底層數組的容量,Slices地址不變。一旦添加元素導致超過底層數組的容量,就會reallocated重新分配。預設底層數組容量為cap(slice)。
特殊文法...(三個點的省略符號)
x := []int{1,2,3}
y := []int{4,5,6}
x = append(x, y...)//去掉...報錯,因為y不是int類型
fmt.Println(x)//有點類似Python對於字典**dict
init函數
每個原始碼檔案都可以有一個init函數,在該原始碼檔案的import語句執行完後,並且變數調用其initializers後執行init函數。一般用於檢查該檔案中的各種狀態是否正確
import for side effect
有時候import一個library,並不是為了使用某個函數或者類,僅僅只是希望該library的init函數執行一次,做一下準備工作:
import _ "net/http/pprof"//during its init function, the net/http/pprof package registers HTTP handlers that provide debugging information
Channel
管道常用於goroutine之間的通訊,同步;Go不推薦使用共用記憶體,互斥元等方式通訊。管道用於goroutine同步例子:
c := make(chan int) // 初始化一個不帶緩衝的管道go func() { list.Sort() c <- 1 // Send a signal; value does not matter.}()//新起一個goroutine,去做排序,排序完後向管道寫入資料doSomethingForAWhile()<-c // 阻塞等待管道有資料寫入,即等待上面goroutine排序完成管道用作semaphore(訊號量),比如限流,例子:
var sem = make(chan int, 100)func Serve(queue chan *Request) {for req := range queue { tmp := req // Create new instance of req for every goroutine. sem <- 1 //訊號量滿時寫入阻塞 go func() { process(tmp) <-sem //處理完一個請求釋放一次訊號量 }()}}由於管道sem分配了buffer,size為100.所以前100個請求,可以不阻塞立即得到執行。後面的請求必須排隊等待前面100個請求當中,有請求處理完畢(管道中有資料被讀出),才能寫入管道,得到處理。所以,這個例子,管道起到限流作用:平行處理的請求數不得超過100
panic recover
Go的異常處理機制,同try...except...
Because recover always returns nil unless called directly from a deferred function, deferred code can call library routines that themselves use panic and recover without failing.
func safelyDo(work *Work) {defer func() { if err := recover(); err != nil { log.Println("work failed:", err) }}()do(work)}do函數裡出了panic異常,執行deferred函數func,func函數裡,調用recover捕獲異常。