這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
1. golang的函數類型轉換
一個go playground的例子,首先定義一個func類型的別名A,然後定義了A的一個方法。再定義一個函數,此函數的參數與傳回值與A相同(這樣才能顯式轉換)。在main中把此函數顯式轉換為A類型,這樣它就可以調用A的方法了。
package mainimport "fmt"type A func(int, int)func (f A)Serve() {fmt.Println("serve2")}func serve(int,int) {fmt.Println("serve1")}func main() {a := A(serve)a(1,2)a.Serve()}
2. golang的http包處理流程
func HelloServer(w http.ResponseWriter, req *http.Request) { io.WriteString(w, "hello, world!\n")} func main() { http.HandleFunc("/hello", HelloServer) err := http.ListenAndServe(":12345", nil) if err != nil { log.Fatal("ListenAndServe: ", err) } }
首先調用Http.HandleFunc
按順序做了幾件事:
1 調用了DefaultServerMux的HandleFunc
2 調用了DefaultServerMux的Handle
3 往DefaultServeMux的map[string]muxEntry中增加對應的handler和路由規則
其次調用http.ListenAndServe(":12345", nil)
按順序做了幾件事情:
1 執行個體化Server
2 調用Server的ListenAndServe()
3 調用net.Listen("tcp", addr)監聽連接埠
4 啟動一個for迴圈,在迴圈體中Accept請求
5 對每個請求執行個體化一個Conn,並且開啟一個goroutine為這個請求進行服務go c.serve()
6 讀取每個請求的內容w, err := c.readRequest()
7 判斷header是否為空白,如果沒有設定handler(這個例子就沒有設定handler),handler就設定為DefaultServeMux
8 調用handler的ServeHttp
9 在這個例子中,下面就進入到DefaultServerMux.ServeHttp
10 根據request選擇handler,並且進入到這個handler的ServeHTTP
mux.handler(r).ServeHTTP(w, r)
11 選擇handler:
A 判斷是否有路由能滿足這個request(迴圈遍曆ServerMux的muxEntry)
B 如果有路由滿足,調用這個路由handler的ServeHttp
C 如果沒有路由滿足,調用NotFoundHandler的ServeHttp
可以看一下這篇文章的介紹:http://www.cnblogs.com/yjf512/archive/2012/08/22/2650873.html
3. golang的閉包一例
package mainimport "fmt"func adder() func(int) int { sum := 0 return func(x int) int { sum += x return sum }}func main() { pos, neg := adder(), adder() for i := 0; i < 10; i++ { fmt.Println( pos(i), neg(-2*i), ) }}
對於以上代碼的正確結果,需要理解閉包中的全域和局部變數,sum是全域變數,x是局部變數,這樣就不會錯了。
運行結果:
0 01 -23 -66 -1210 -2015 -3021 -4228 -5636 -7245 -90
4. 擷取golang的各種路徑
使用者當前所在路徑:
os.Getwd()
執行程式檔案相對路徑:
file, _ := exec.LookPath(os.Args[0])
package mainimport( "os" "log" "os/exec" "path")func main() { file, _ := os.Getwd() log.Println("current path:", file) file, _ = exec.LookPath(os.Args[0]) log.Println("exec path:", file) dir,_ := path.Split(file) log.Println("exec folder relative path:", dir) os.Chdir(dir) wd, _ := os.Getwd() log.Println("exec folder absolute path:", wd)}
可執行檔我放在/home/lm/handcode/test
我執行的路徑是/home/lm/
[lm@openstack ~]$ handcode/test2013/02/06 11:09:07 current path: /home/lm2013/02/06 11:09:07 exec path: handcode/test2013/02/06 11:09:07 exec folder relative path: handcode/2013/02/06 11:09:07 exec folder absolute path: /home/lm/handcode
5. golang的Error()
可以為自訂對象定義自己的Error()、String()方法,從而輸出指定資訊:
package mainimport ("fmt""math")type ErrNegativeSqrt float64func (e ErrNegativeSqrt) Error() string {return "cannot Sqrt negative number:" + fmt.Sprint(float64(e))}func Sqrt(f float64) (float64, error) {if f < 0 {return 0, ErrNegativeSqrt(f)}z := float64(1)for {y := z - (z*z-f)/(2*z)if math.Abs(y-z) < 1e-10 {return y, nil}z = y}return z, nil}func main() {fmt.Println(Sqrt(2))fmt.Println(Sqrt(-2))}
當f<0時把f顯式轉換成 ErrNegativeSqrt類型,傳入到error,就會調用自訂的ErrNegativeSqrt的Error()方法。
6. golang匯入自己的package
以前寫的go程式都是在main包中,即使有多個go檔案,其開頭都是package main,所以它們都屬於main包。今天實驗了一下其它自定一包的匯入:定義了fsnotify.go和fsnotify_linux.go,這兩個檔案屬於package fsnotify;定義main.go,屬於package main。main.go會調用fsnotify包中的方法,那麼怎樣匯入fsnotify包呢?
在fsnotify包沒有被正確匯入時,會提示如下錯誤:
[root@localhost src]# go build main.gomain.go:5:5: cannot find package "fsnotify" in any of:/software/go/src/pkg/fsnotify (from $GOROOT)/software/fsnotify/src/fsnotify (from $GOPATH)
也就是說在main.go中的import "fsnotify"首先根據$GOROOT找,如果找不到再根據$GOPATH找,還找不到的話就提示以上錯誤。先執行go env看一下go對應的環境變數:
[root@localhost software]# go envGOARCH="amd64"GOBIN=""GOCHAR="6"GOEXE=""GOHOSTARCH="amd64"GOHOSTOS="linux"GOOS="linux"GOPATH="/software/fsnotify"GORACE=""GOROOT="/software/go"GOTOOLDIR="/software/go/pkg/tool/linux_amd64"TERM="dumb"CC="gcc"GOGCCFLAGS="-g -O2 -fPIC -m64 -pthread"CXX="g++"CGO_ENABLED="1"
所以正確的做法是在$GOROOT/src/pkg下建立一個與自訂包名相同的檔案夾,然後把相應檔案放到這個檔案夾下;或者在$GOPATH/src下建立一個與自訂包名相同的檔案夾,把相應檔案放進去,就OK了。一般情況下都是使用第二種做法。
$GOPATH可以定義多個路徑,與$PATH的道理一樣,可以在/root/.bashrc中這樣定義$GOPATH:
GOPATH=/software/fsnotify:/software/tmpexport GOPATH
然後把之前的fsnotify檔案夾mv到/software/tmp/src下,go build main.go依然成功,以上兩點得到驗證。
7. golang的項目目錄結構
一般的,一個go項目在$GOPATH下會有如下3個目錄:
——bin——pkg——src
其中,bin存放編譯後的可執行檔;pkg存放編譯後的包檔案;src存放項目源檔案。一般bin和pkg目錄可以不建立,go命令會自動建立(如 go install),只需要建立src目錄即可。對於pkg目錄,pkg中的檔案是Go編譯產生的,而不是手動放進去的。(一般檔案尾碼.a)對於src目錄,用來存放源檔案,Go中源檔案以包(package)的形式組織。通常,建立一個包就在src目錄中建立一個檔案夾。
比如建立一個名為test的go項目,初始目錄如下:
test/|——install|——src
install內容如下:
#!/usr/bin/env bashif [ ! -f install ]; thenecho 'install must be run within its container folder' 1>&2exit 1fiCURDIR=`pwd`OLDGOPATH="$GOPATH"export GOPATH="$CURDIR"gofmt -w srcgo install testexport GOPATH="$OLDGOPATH"echo 'finished'
之所以加上這個install,是不用配置GOPATH(避免新增一個GO項目就要往GOPATH中增加一個路徑)
接下來,增加一個包:config和一個main程式。目錄結構如下:
test|-- install`-- src |-- config | `-- config.go `-- test `-- main.go
注意config.go中的package名稱最好和目錄config一致,而檔案名稱可以隨便。main.go表示main包,檔案名稱建議為main.go。(註:不一致時,產生的.a檔案名稱和目錄名一致,這樣,在import 時,應該是目錄名,而引用包時,需要包名。例如:目錄為myconfig,包名為config,則生產的靜態包檔案是:myconfig.a,引用該包:import “myconfig”,使用包中成員:config.LoadConfig())
config.go代碼:
package configfunc LoadConfig() {}
main.go代碼:
package mainimport ("config""fmt")func main() {config.LoadConfig()fmt.Println("Hello, GO!")}
在項目根目錄執行./install
這時候的目錄結構為:
test|-- bin| `-- test|-- install|-- pkg| `-- linux_amd64| `-- config.a`-- src |-- config | `-- config.go `-- test `-- main.go
其中config.a是包config編譯後產生的;bin/test是產生的二進位檔案
這個時候可以執行:bin/test了。會輸出:Hello, GO!
包可以多層目錄,比如:net/http包,表示源檔案在src/net/http目錄下面,不過源檔案中的包名是最後一個目錄的名字,如http
而在import包時,必須完整的路徑,如:import “net/http”。
go build與go install的區別:
go build:在臨時目錄下建立包編譯後的二進位檔案,該命令不會將二進位檔案安裝到 bin、pkg目錄;go build後面直接接需要編譯的檔案名稱。
go install:和 go build 參數相同,唯一的區別在於將編譯結果拷貝到 bin、pkg目錄中;go install需要建立一個與項目目錄同名的子目錄,然後把main.go放進去;go install接項目目錄名。