# GO 筆記 ### 連接字串 用 += 會創造一個新字串,比較高效的作法是用 join ### 取得 argv os.Args[0] 是檔案名,後面會接參數 ### 註解好習慣 main 前面寫一下這個程式是用來幹麻的 function 前面寫一下這函式是用來幹麻的 ### 控制語句初始化 可以在所有控制語句前面嵌入賦值語句,例如 ```=go if err := r.ParseForm(); err != nil { log.Print(err) } ``` 可以限制作用域順便初始化,潮 ### 找 function 怎麼用不一定要上網 `go doc http.ListenAndServe` 就可以在本地端看 ### 存取權限 go 預設直接用開頭大寫還是小寫來規定 method 跟 value 的存取權限 開頭大寫英文就是 public 開頭小寫英文就是 private 區域變數長度越小越好,縮寫全部大寫 ### 變數宣告 `var 名子 類型 = 表達式` 類型跟表達式可以省略其中一個,省略表達的話內建用 0,nil,false 初始化 ### 類別定義 ```=go gopl.io/ch2/tempconv0 // Package tempconv performs Celsius and Fahrenheit temperature computations. package tempconv import "fmt" type Celsius float64 // 攝氏溫度 type Fahrenheit float64 // 華氏溫度 const ( AbsoluteZeroC Celsius = -273.15 // 絶對零度 FreezingC Celsius = 0 // 結冰點溫度 BoilingC Celsius = 100 // 沸水溫度 ) func CToF(c Celsius) Fahrenheit { return Fahrenheit(c*9/5 + 32) } func FToC(f Fahrenheit) Celsius { return Celsius((f - 32) * 5 / 9) } var c Celsius = ``` 上面的宣告不會轉換值,但會轉換語意,宣告時必須顯示轉換才吃,宣告方式類似 C-style 的型態轉換。 此外底層保留相同數據類型,所以像上面的做法 float 的算術運算還是可以使用,但底層相同語意上不同類型的數值無法比較。 如果轉換失敗會在編譯時就吐出來,運行時不會發生轉換失敗。 ### 類別方法定義 ```=go func (c Celsius) String() string { return fmt.Sprintf("%g°C", c) } ``` 這樣表示聲明的是 Celsius 的方法。 ### append slice = append(slice, anotherSlice...) append 後面原本要接的是元素,如果後面變數後面加上 `...` 則會自動將 slice 展開, hen 神奇 ### go slice in go,evry thing is pass by value 就算 pointer 也是這樣 slice 本身就是一個 pointer ,所以傳入的時候也是把這個 pointer 複製一份傳過去,如果這時候只是對這個 pointer pointer = append 並不會修改到原本的部份,因為他其實是在那個複製的 pointer 位置塞上了另外一個指標,但傳入前的依然指向原本的地方,舉例如下 ```go package main import "fmt" type mystruct struct { val int } func main() { var s1 []mystruct var s2 []mystruct onlyPassing(s1) s2 = returningToo(s2) fmt.Printf("%#v\n%#v", s1, s2) } func onlyPassing(s []mystruct) { s = append(s, mystruct{1}) } func returningToo(s []mystruct) []mystruct { s = append(s, mystruct{1}) return s } //[]main.mystruct(nil) //[]main.mystruct{main.mystruct{val:1}} ``` 所以要改要不然在前面改,要不然逐格改內容,要不然用 `copy` ,要不然傳 pointer to pointer 以下 copy 使用範例 ```=go //leetcode 189. Rotate Array func rotate(nums []int, k int) { copy(nums,append(nums[len(nums) - k % len(nums):],nums[:len(nums) - k % len(nums)]...)) return } ``` ### 無型態的 const 無型態的 const 很方便,這樣才不會每次用到的時候都要轉型 他允許了編譯器做有限度的隱式轉換 ### itoa 一個自動增加序列的宣告,說不定我們 protoc 可以用 ### init() packet 內的 init 預設會在引入時就執行,所以我應該不用手動寫一堆 init 了 這邊可以修改一下 ### 無限參數 Go 可以接受同一型態無限多的參數,只要這樣寫就可 ```go= func test(x ... int) int { // x is a []int for v:=x range { fmt.Println(x) } } ``` Go 只是自動幫忙把東西轉換成 []Type 而已,就是個語法糖 ### interface interface 也是一種類別,可以將參數轉換為 interface 的 type 之後會自動轉換為該 interface 類別,然後可以用 switch h.(type) 來針對類別分別處理, 或是呼叫該類別共通實作的 function,只要該 struct 中的任何一個成員有實作該 interface 即可 (該成員必須是 Embedded structs,就他媽是一個繼承的概念,多重繼承是允許的,但只能有一個繼承者實作該界面,不然編譯器會不知道自己該連結哪個 func) 我覺得這個設計猛猛的XD ```go= // https://play.golang.org/p/rZH2Efbpot func (s secretAgent) speak() { fmt.Println("I am", s.first, s.last, " - the secretAgent speak") } func (p person) speak() { fmt.Println("I am", p.first, p.last, " - the person speak") } type human interface { speak() } func bar(h human) { switch h.(type) { case person: fmt.Println("I was passed into barrrrrr", h.(person).first) case secretAgent: fmt.Println("I was passed into barrrrrr", h.(secretAgent).first) } h.speak() } ``` ### return function 紀錄下語法 ```go= func bar() func() int { return func() int { return 451 } } ``` ### Method sets Method sets determine what methods attach to a TYPE. It is exactly what the name says: What is the set of methods for a given type? That is its method set. Receivers Values ----------------------------------------------- (t T) T and *T (t *T) *T 所以如果我對某 obj 的指針指定了 method sets,那該 obj 的「物件」是不能使用該 method set 的, 但如果我對該 obj 的物件指定了 method sets,指針跟物件則都可以使用。 ```go= type circle struct { radius float64 } type shape interface { area() float64 } func (c *circle) area() float64 { return math.Pi * c.radius * c.radius } func info(s shape) { fmt.Println("area", s.area()) } func main() { c := circle{5} d := &circle{5} c.area() // work d.area() // work info(c) // not work info(d) // work } ``` ### http server 不要用 ``` http.HandleFunc("/", myHandler) // 设置访问路由 log.Fatal(http.ListenAndServe(":8080", nil)) ``` 這樣連 timeout 都沒辦法設 ### panic catch painc 會強制輸出到 stderr 在 linux 上可以用 syscall 的 dup2 來處理,或是在執行時就從 os 重新導向 ## Visual code attach 流程 Build 的時候就要關閉優化,這樣才能在 attach 的時候正常拿到值 `go build -gcflags=all="-N -l" main.go` 首先要在該 attach 的目標上起一個 debugger ,我們不使用 GDB (對 goroutine 的支援不好),我們使用 dlv dlv 可以列出在每個 goroutine 上的物件的內容,更詳細好用。 執行環境上必須有 go ,安裝 dlv 很簡單只要一行 `go get github.com/derekparker/delve/cmd/dlv` 要 GOPATH 有正確加入到 PATH 才能抓到執行檔,輸入 dlv 就能測試 ![](https://i.imgur.com/ERn3nqB.png) 實際要 attach 時要先開 attach 用的 port 及啟動 dlv `dlv attach ${PID} --headless=true --api-version=2 --listen=:2345 --log` > 查找 pid 可以用 ps aux | grep -i "prog" > 直接在外層機器找就可以,不用進入 docker ,因為 docker 本身的 process 其實也是暴露出來的 attach 進去的時候會先暫停程式,直到 visual code 真的連上為止 接著在 visual code 裡面設定 attach 設定 ``` { "name": "Attach", "type": "go", "request": "launch", "mode": "remote", "remotePath": "${workspaceRoot}", "port": 2345, "host": "${remotePort}", "program": "${workspaceRoot}", "env": {}, "args": [], "showLog": true } ``` > remotePort 請改為 attach 的目的地。 接著往 DEBUG 戳下去就行了 ![](https://i.imgur.com/WKsAkwe.png) 連進去之後案暫停就可以看到資訊 也可以設斷點 左下角的 callstack 可以看到每一個 goroutine 的 callstack 每個 callstack 都可以點進去查看該 callstack 的變數內容 ![](https://i.imgur.com/JqnQtdB.png) 預設只能看到第一層的變數內容,要往下看要在 dlv cli 輸入 `p ${value}` visual code 對應的行為是在左上角 VARIABLES 案右鍵選 add to watch 然後就能看到內部訊息了 ![](https://i.imgur.com/CcNQqtM.png) ### docker 額外配置 Linux/Docker Docker has security settings preventing ptrace(2) operations by default within the container. Solution: To run your container insecurely, pass --security-opt=seccomp:unconfined to docker run when starting. Reference: derekparker/delve/515 確認有效 `docker run --security-opt seccomp=unconfined ` 即可 ### visual code local value 卡住 關掉在重開該 view 就好 ### linux 連 windows debug 的解決方法 https://github.com/Microsoft/vscode-go/issues/2242 `remotePath` 對方電腦上的原始檔 `program` 我方檔案位置 映射過來有可能會錯,可以用 link 來處理 設定 link `mklink /j RouDataServer D:\go_project\src\RouDataServer` ### dump file 參考教學 https://segmentfault.com/a/1190000010684345 vs code 目前看起來不支援驗屍的功能,attach 進去看到的 call stack 不完整 要手動用 dlv 去看 ### 閉包 ```=go package main import ( "fmt" "time" ) func main() { input := []int{1, 2, 3, 4} for _, v := range input { go func() { fmt.Println(v) }() } time.Sleep(100 * time.Millisecond) } ``` 會全部印出 4 GO parse JSON very slow use two array to solve this ## CGO struct https://stackoverflow.com/questions/36195935/too-few-values-in-struct-initializer-when-initialize-c-struct-in-golang 太猛惹 ## := this is workable ``` package main import ( "fmt" "strconv" ) func main() { i1, err := strconv.ParseInt("55", 10, 64) if err == nil { fmt.Println(i1) } i2, err := strconv.ParseInt("66", 15, 64) if err == nil { fmt.Println(i2) } } ``` ## go install will build static lib compile faster ## RVO Go also have RVO ``` package main import ( "fmt" ) type myst struct { } func test() myst { p := myst{} fmt.Printf("%p\n", &p) return p } func main() { t := test() fmt.Printf("%p\n", &t) } ``` no rvo ```go package main import ( "fmt" ) type myst struct { a int } func test() myst { p := myst{} fmt.Printf("%p\n", &p) return p } func main() { t := test() fmt.Printf("%p\n", &t) } ``` when can we have rvo ? https://en.cppreference.com/w/cpp/language/as_if ## Pass to method ```go func (m MyType) test1() { } func (m *MyType) test1() { } ``` Both work. But first struct is copy by value