# 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 就能測試

實際要 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 戳下去就行了

連進去之後案暫停就可以看到資訊
也可以設斷點
左下角的 callstack 可以看到每一個 goroutine 的 callstack
每個 callstack 都可以點進去查看該 callstack 的變數內容

預設只能看到第一層的變數內容,要往下看要在 dlv cli 輸入 `p ${value}`
visual code 對應的行為是在左上角 VARIABLES 案右鍵選 add to watch
然後就能看到內部訊息了

### 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