Declare
var t *T = new(T) // t := new(T)
var a uint64 = 22 // a := uint64(22)
a := 0x12 // 18
Multiple variables
ff, xx := 3, "cc"
[]interface{}
hMap := make(map[string]interface{})
hMap["ID"] = "06"
hMap["Info"] = map[string]string{
"Name": "Jack"
}
resque2 := map[string]interface{}{
"class": "hnap",
"args": []interface{}{hMap},
}
map[string]interface{}
x := map[string]interface{}{
"foo": []string{"a","b"},
"bar": "foo",
"baz": 10.4,
}
map[string]interface{}
t := map[string]interface{}{}
t["id"] = 312
t["type"] = "realtime"
t["data"] = []map[string]string{
{
"did": "did1",
"action": "action1",
},
{
"did": "did2",
"action": "action2",
},
}
但不可以, 無法這樣給值
var t map[string]interface{}
t["xxx"] = "xxx"
map[string]map[string]string
elements := map[string]map[string]string{
"A": map[string]string{
"field1": "val1",
"field2": "val2",
},
"B": map[string]string{
"field1": "val1",
"field2": "val2",
},
}
[]struct
type Target struct {
Topic string `json:"topic"`
Message string `json:"message"`
}
type Payload struct {
Targets []Target `json:"targets"`
From string `json:"from"`
}
var payload = Payload{
Targets: []Target{
Target{
Topic: "topic1",
Message: "message1",
},
Target{
Topic: "topic2",
Message: "message2",
},
},
From: "api",
}
Declare empty slice - var
vs :=
宣告出來為 nil, 長度 0 (建議)
var q []string
宣告出來為 []
, 長度 0, 如果要 json encode 想避免生成 null
的話, 建議使用這個
q := []string{}
const 對應 int (有點像 enum)
const (
LevelCritical = iota // 0
LevelError
LevelWarning
LevelNotice
LevelInfo // 4
LevelDebug
)
Pointer
&
: 對變數取址
*
: 對指針取值, 將指標取回一般變數
Example :
package main
import "fmt"
func main() {
var a int = 1
var b *int = &a
var c **int = &b
var x int = *b
fmt.Println("a = ",a) // 1
fmt.Println("&a = ",&a) // &a = 0xf840037100
fmt.Println("*&a = ",*&a) // *&a = 1
fmt.Println("b = ",b) // b = 0xf840037100
fmt.Println("&b = ",&b) // &b = 0xf840037108
fmt.Println("*&b = ",*&b) // *&b = 0xf840037100
fmt.Println("*b = ",*b) // *b = 1
fmt.Println("c = ",c) // c = 0xf840037108
fmt.Println("*c = ",*c) // *c = 0xf840037100
fmt.Println("&c = ",&c) // &c = 0xf840037110
fmt.Println("*&c = ",*&c) // *&c = 0xf840037108
fmt.Println("**c = ",**c) // **c = 1
fmt.Println("***&*&*&*&c = ",***&*&*&*&*&c) // ***&*&*&*&c = 1
fmt.Println("x = ",x) // x = 1
}
什麼時候用指標?
- When calling struct’s function by value, it copy itself. If struct is big, use pointer.
- If function has to changed its struct’s value outside, use pointer.
- Make code consistent (if call by value or reference mixed up), use pointer.
指標行為
code :
func main() {
a := QQ{}
b := &a
c := *b
fmt.Printf("a: %p\n", &a)
fmt.Printf("b: %p (address same as a) \n", b)
fmt.Printf("c: %p (different address from a, b)\n", &c)
d := xx()
fmt.Printf("d: %p (different address from x)\n", &d)
e := zz()
fmt.Printf("e: %p (different address from z)\n", &e)
}
func xx() QQ {
x := QQ{}
fmt.Printf("x: %p (return instance)\n", &x)
return x
}
func zz() *QQ {
z := QQ{}
fmt.Printf("z: %p (return pointer)\n", &z)
return &z
}
如果 struct 定義沒有欄位, 結果 :
type QQ struct {}
a: 0x1127a88
b: 0x1127a88 (address same as a)
c: 0x1127a88 (different address from a, b)
x: 0x1127a88 (return instance)
d: 0x1127a88 (different address from x)
z: 0x1127a88 (return pointer)
e: 0xc42000c030 (different address from z)
如果 struct 定義有欄位, 結果 :
type QQ struct {
Name string
}
a: 0xc42000e290
b: 0xc42000e290 (address same as a)
c: 0xc42000e2a0 (different address from a, b)
x: 0xc42000e2e0 (return instance)
d: 0xc42000e2d0 (different address from x)
z: 0xc42000e2f0 (return pointer)
e: 0xc42000c030 (different address from z)
關於回傳 instance or pointer 的記憶體 : call func 拿到的都是新的記憶體, 不管 func 裡面回傳的是不是指標
dereference pointer
Error invalid operation: cannot index this.small (variable of type *[]int)
var small *[]int
value := this.small[0] // doesn't work
value := (*this.small)[0] // work
swtich
map + switch
m := map[string]int{"foo":1}
f := func(key string) bool { _, ok := m[key]; return ok }
switch {
case f(key):
// whatever
or
switch category {
case
"auto",
"news",
"sport",
"music":
return true
}
斷言 (type assertion)
分辨型別
var anything interface{} = "string"
switch v := anything.(type) {
case string:
fmt.Println(v)
case int32, int64:
fmt.Println(v)
case interface{}:
fmt.Println(v)
default:
fmt.Println("unknown")
}
已知道是什麼型別
value, ok := a.(string)
if !ok {
fmt.Println("It's not ok for type string")
return
}
// or
if str, ok := a.(string); ok {
轉型
int to int64
i64 := int64(23)
int to float64
i := 5;
f := float64(i)
int to string
s := strconv.Itoa(123)
int64 to int
i := int64(5)
int(i)
int64 to string
s := strconv.FormatInt(int64(5), 10)
int64 to float64
float64(1)
int64 to uint64
u, err := strconv.ParseUint(strconv.FormatInt(int64(123), 10), 10, 64)
float64 to string
s64 := strconv.FormatFloat(v, 'E', -1, 64)
float64 to int64
var f float64 = 55.3
i = int64(f)
float64 to uint
uint(user["age"].(float64))
string to byte
dd := "dcf"
fmt.Println([]byte(dd))
result : [100 99 102]
string to int
v, err = strconv.Atoi(s)
string to int64
v, err := strconv.ParseInt(s, 10, 64)
string to uint32/uint64
v := "42"
if s, err := strconv.ParseUint(v, 10, 32); err == nil {
fmt.Printf("%T, %v\n", s, s)
}
if s, err := strconv.ParseUint(v, 10, 64); err == nil {
fmt.Printf("%T, %v\n", s, s)
}
string to float64
v, err := strconv.ParseFloat("55.74", 64)
*string to string
value := *pointer_str
byte to string
s := string(byteArray)
s := string(byteArray[:])
rune to string
r := rune('a')
fmt.Println(reflect.TypeOf(r)) // int32
fmt.Println(r, string(r)) // 97 a
array to slice
x[:]
interface{} to int
a := job["retryTimes"].(int)
[]interface{} 轉成 interface{}
interface{}([]interface{}{reactor, sensor})
[]interface{} to []int ref : InterfaceSlice
var dataSlice []int = foo()
var interfaceSlice []interface{} = make([]interface{}, len(dataSlice))
for i, d := range dataSlice {
interfaceSlice[i] = d // 或用 append 的方式
}
interface{} conver to bytes.Buffer
switch v := any.(type) {
case bytes.Buffer: // return as is
return v.String() // Here v is of type bytes.Buffer
}
interfacer{} (*Server) to (*Server)
type Server struct {
Name string
}
var v interface{}
if v == nil {
v = &Server{Name: "xx"}
}
fmt.Println(reflect.TypeOf(v)) // *main.Server
s := v.(*Server)
fmt.Println(s.Name) // xx
map[string]interface{} 變成陣列 []interface{}
[]interface{}{reactor, sensor}
struct to another struct
type A struct{ Name string }
type B struct{ Name string }
a := A{"aa"}
b := B(a)
b.Name = "bb"
fmt.Println(a)
fmt.Println(b)
bytes to io.Reader
bytes.NewReader(b)
bytes.Buffer to io.Writer
var b bytes.Buffer
writer := bufio.NewWriter(&b)
strings to io.Reader
strings.NewReader(s)
file to bytes
b, err := ioutil.ReadFile("/tmp/ff.tmp")
file to io.Writer
file, err = os.Open("/tmp/ff.tmp")
defer file.Close()
image to bytes
buf := new(bytes.Buffer)
err := jpeg.Encode(buf, new_image, nil)
send_s3 := buf.Bytes()
convert to another interface
dimg, ok := img.(draw.Image)
指標(pointer)轉實體
*f is msg := sqs.Message(*m)
(*f)["cc"]
(map[string]interface{})(*f)["cc"]
msg := sqs.Message(*m)
a := (map[string]interface{})(args[0].(interface{}).(map[string]interface{}))
loop
Infinite
for {
// ...
}
Range, like other languages
for i:=1; i<=5; i++ {
// ...
}
Until the specific time
for time.Now().Unix() < 1481089195 {
time.Sleep(1 * time.Second)
}
slice
結構
[]byte
---------
ptr | *elem |
---------
len | int |
---------
cap | int |
---------
a slice with five elements
s := make([]byte, 5) // 指定 len, 不指定 cap
[]byte
---------
ptr | | -> [5] bytes | 0 | 0 | 0 | 0 | 0 |
---------
len | 5 |
---------
cap | 5 |
---------
nil slice
var s []byte
[]byte
---------
ptr | nil |
---------
len | 0 |
---------
cap | 0 |
---------
cap and len
- cap: capacity of the underlying array.
- len: how many items are in the array.
The slice abstraction in Go will resize the underlying array for you.
Example:
s := make([]int, 0, 3)
for i := 0; i < 5; i++ {
s = append(s, i)
fmt.Printf("cap %v, len %v, %p\n", cap(s), len(s), s)
}
output:
cap 3, len 1, 0xc000088000
cap 3, len 2, 0xc000088000
cap 3, len 3, 0xc000088000
cap 6, len 4, 0xc00006a030
cap 6, len 5, 0xc00006a030
Code
// var numbers []int
// numbers := make([]int, 5)
for i := 0; i < 5; i++ {
numbers = append(numbers, i)
fmt.Printf("address: %p, length: %d, capacity: %d, items: %v\n", numbers, len(numbers), cap(numbers), numbers)
}
var numbers []int:
2019-09-30 12:58:34 jack@jack-lin /tmp $ go run slice.go
address: 0xc0000140c0, length: 1, capacity: 1, items: [0]
address: 0xc0000140f0, length: 2, capacity: 2, items: [0 1]
address: 0xc000094000, length: 3, capacity: 4, items: [0 1 2]
address: 0xc000094000, length: 4, capacity: 4, items: [0 1 2 3]
address: 0xc00009a000, length: 5, capacity: 8, items: [0 1 2 3 4]
numbers := make([]int, 5):
2019-09-30 12:58:44 jack@jack-lin /tmp $ go run slice.go
address: 0xc000096000, length: 6, capacity: 10, items: [0 0 0 0 0 0]
address: 0xc000096000, length: 7, capacity: 10, items: [0 0 0 0 0 0 1]
address: 0xc000096000, length: 8, capacity: 10, items: [0 0 0 0 0 0 1 2]
address: 0xc000096000, length: 9, capacity: 10, items: [0 0 0 0 0 0 1 2 3]
address: 0xc000096000, length: 10, capacity: 10, items: [0 0 0 0 0 0 1 2 3 4]
基本操作
append item
list = append(list, item)
append slice
list = append(list, list2...)
其他
Remove an item with a specific index from an array
slice = append(slice[:index], slice[index+1:]...)
重新切一個 slice, 新 slice 會使用原本 slice 的底層, 改成使用 copy
才會是一個新的 slice
func main() {
data := get()
fmt.Println(len(data), cap(data), &data[0])
}
func get() []byte {
raw := make([]byte, 10000)
fmt.Println(len(raw), cap(raw), &raw[0])
return raw[:3]
}
// 10000 10000 0xc42005e000
// 3 10000 0xc42005e000 (記憶體位置一樣)
func get() (res []byte) {
raw := make([]byte, 10000)
fmt.Println(len(raw), cap(raw), &raw[0])
res = make([]byte, 3)
copy(res, raw[:3])
return
}
// 10000 10000 0xc42005e000
// 3 3 0xc42000e280
array vs slice
差別主要是 array 是有固定長度, slice 沒有
code:
// array
var List = [2]int{1, 2} // or [...]int{1,2}
copyList := List
copyList[1] = 4
fmt.Printf("new=%v, old=%v\n", copyList, List)
// slice
var oldSlice = []int{1, 2, 3, 4}
newSlice := oldSlice
newSlice[3] = 10
newSlice = append(newSlice, 100)
fmt.Printf("new=%v, old=%v\n", newSlice, oldSlice)
result:
new=[1 4], old=[1 2]
new=[1 2 3 10 100], old=[1 2 3 10]
array 是直接操作記憶體位置
傳遞 slice 是傳遞記憶體位置
感謝 chris:
- 記得 slice map 的變數 只是一個參照
- slice 跟 map 的變數 參照的 值都是隔開的
- 但是底層運作是使用 指標
- 裡面真正的值 是存在記憶體
- 關鍵字: 淺拷貝/深拷貝
Example
func main() {
d := []string{"xxx"}
fmt.Println("original:", d)
nonPointer(d)
fmt.Println("non-pointer:", d)
change(d)
fmt.Println("change:", d)
pointer(&d)
fmt.Println("pointer:", d)
}
func nonPointer(d []string) {
d = append(d, "vvvv")
}
func change(d []string) {
d[0] = "ccc" // 可修改到外部
}
func pointer(d *[]string) {
*d = append(*d, "vvvv")
}
Result
original: [xxx]
non-pointer: [xxx]
change: [ccc]
pointer: [ccc vvvv]
Example 2
func main() {
d := [][]string{
[]string{"foo", "bar"},
[]string{"foo1", "bar1"},
}
byValue(d)
fmt.Println(d) // [[foo bar] [foo1 baz1]]
byRef(&d)
fmt.Println(&d) // &[[foo bar] [foo1 baz2]]
}
func byValue(d [][]string) {
d[1][1] = "baz1"
fmt.Println(d) // [[foo bar] [foo1 baz1]]
}
func byRef(d *[][]string) {
(*d)[1][1] = "baz2"
fmt.Println(d) // &[[foo bar] [foo1 baz2]]
}
Array is passed by value by default, not like slice by reference by default
func main() {
d := [2]string{"foo", "bar"}
byValue(d)
fmt.Println(d) // foo, bar
byRef(&d)
fmt.Println(&d) // foo, baz
}
func byValue(d [2]string) {
d[1] = "baz"
fmt.Println(d) // foo, baz
}
func byRef(d *[2]string) {
d[1] = "baz"
fmt.Println(d) // foo, baz
}
宣告獨立乾淨的 slice
不論是參數傳遞 (call by value) 還是 assign 給一個新的變數, *elem (pointer) 都會指向同一個, 而改其中一個也會改動到另一個
如果沒有 append 新的 item, 只改變原本 slice 長度內的值 e.g. s[2] = 3 (如果s總長度是5), 都會一直是共用指標
如果要打破這個規則, 就將其中一個 append 新的 item 那麼 *elem (pointer) 就會是不同的
如果要將 slice 指給一個新的變數, 但又不想要將原本的 *elem (pointer) 參照到原本的變數, 除了用 make 也可以 new 全新的再用 append 給值
code
s := []int{1, 2, 3}
// Old *elem
s1 := s
// New *elem
s2 := s
s2 = append(s2, 4)
// New *elem
s3 := []int{}
s3 = append(s3, s...)
// New *elem
s4 := make([]int, 0)
s4 = append(s4, s...)
s[1] = 999
fmt.Println(s) // [1 999 3]
fmt.Println(s1) // [1 999 3]
fmt.Println(s2) // [1 2 3 4]
fmt.Println(s3) // [1 2 3]
fmt.Println(s4) // [1 2 3]
check if two slices are the same
reflect.DeepEqual(tmp, item.ans)
map others
傳入接收 actionData map[string]interface{}
參數的 func
send_var(jobData["ActionData"].(map[string]interface{}))
func send_var(actionData []interface{}) (err error) {
fmt.Println(actionData[0].(map[string]interface{})["Did"])
[]map[string]interface{} 傳入型態接受 []interface{} 的 func
test(&map[string]interface{})
...
func test(rec *[]interface{})
用指標方式傳入可避免型態轉換發生的問題
map[string]interface{} 一直輸出它的 index 順序不一定會一樣
qq := map[string]interface{}{
"0": "111111",
"1": "xxxxxxxx",
}
[]interface{} 一直輸出它的 index 順序會一樣
用 mi 定義 type, 讓 code 更乾淨
type mi map[string]interface{}
res["Envelope"].(interface{}).(mi)["Body"].(interface{}).(mi)[actionData["Name"].(string)+"Response"].(interface{})
assignment to entry in nil map
: 會發生此原因是你在賦值給 map 時沒有初始化
var d []map[string]interface{}
(Wrong)
var f map[string][]map[string]interface{}
f["ss"] = d
(Correct)
f := map[string][]map[string]interface{}{}
f["ss"] = d
(Correct)
var f map[string][]map[string]interface{}
f = make(map[string][]map[string]interface{})
f["ss"] = d
map 是否為空
if len(map) == 0 {
....
}
判斷 key 是否存在
a := map[string]interface{}{
"a": "A",
}
if t1, matched := a["a"]; matched {
fmt.Println(t1.(string))
}
if t2 := a["a"]; t2 != nil {
fmt.Println(t2.(string))
}
判斷 key 及型態
a := map[string]interface{}{"fff": "xx"}
ff, ok := a["fff"].(int) // 即使來源跟對象的型態不一樣, 不會造成 panic
if !ok {
fmt.Println("no")
// return
}
fmt.Println(ff)
// result
no
0
Slice Tricks
判斷 slice key 是否存在
a := []string{"zero", "one", "two"}
fmt.Println(a, len(a))
x, v := 3, "nothing"
if len(a)-1 >= x {
v = a[x]
}
fmt.Printf("%s", v)
type *map[string]interface {}
does not support indexing
func tt(dd *map[string]interface{}) {
(*dd)["qq"] = "qqqq"
}
傳遞 map 是傳址非傳值
func main() {
dd := map[string]interface{}{
"ff": "ffff",
}
fmt.Printf("original, mem: %p val: %v\n", &dd, dd)
nonPointer(dd)
fmt.Printf("non-pointer, val: %v\n", dd)
change(dd)
fmt.Printf("change, val: %v\n", dd)
pointer(&dd)
fmt.Printf("pointer, val: %v\n", dd)
newMap(dd)
fmt.Printf("newMap, val: %v\n", dd)
makeMap(dd)
fmt.Printf("makeMap, val: %v\n", dd)
}
// 會修改到外部
func nonPointer(dd map[string]interface{}) {
fmt.Printf("non-pointer, mem: %p\n", &dd)
dd["dd"] = "ddd"
}
// 會修改到外部
func change(dd map[string]interface{}) {
fmt.Printf("non-pointer, mem: %p\n", &dd)
dd["ff"] = "cccc"
}
// 會修改到外部
func pointer(dd *map[string]interface{}) {
fmt.Printf("pointer, mem: %p\n", &dd)
(*dd)["qq"] = "qqqq"
}
// 即使 qq 裡, new 一個新變數 ff, 並將 dd 的值給 ff, 修改仍會改到外面傳進來的 map
func newMap(dd map[string]interface{}) {
ff := dd
ff["ff"] = "zzzz"
}
// 必須要用 make new 一個實體, 才不會參照到原本的記憶體位址
func makeMap(dd map[string]interface{}) {
cc := make(map[string]interface{})
for k, v := range dd {
cc[k] = v
}
cc["bbb"] = "bbb"
}
結果
original, mem: 0xc42000c028 val: map[ff:ffff]
non-pointer, mem: 0xc42000c038
non-pointer, val: map[ff:ffff dd:ddd]
non-pointer, mem: 0xc42000c040
change, val: map[ff:cccc dd:ddd]
pointer, mem: 0xc42000c048
pointer, val: map[ff:cccc dd:ddd qq:qqqq]
newMap, val: map[dd:ddd qq:qqqq ff:zzzz]
makeMap, val: map[dd:ddd qq:qqqq ff:zzzz]
func params
傳入/接收 map pointer
error: (type *map[string]interface {} does not support indexing)
solution:
func main() {
a := map[string]interface{}{
"xxx": "XXX",
}
qq(&a)
}
func qq(tmp *map[string]interface{}) {
fmt.Println((*tmp)["xxx"])
}
傳入/接收 map pointer
var args = map[string]interface{}{
"A": "1",
"B": "2",
}
foo(&args)
func foo(args *map[string]interface{}) {
options := (map[string]interface{})(*args)
fmt.Println(options["A"])
}
foo(args)
func foo(name string, ...args map[string]interface{}) {
options := (map[string]interface{})(args[0])
fmt.Println(options["A"])
}
傳入 Optional 參數
T1("A", "B")
func T1(str ...string) {
fmt.Println(str[0]) // A
}
T2([]string{"A", "B"})
func T2(str ...[]string) {
fmt.Println(str[0]) // [A B]
}
map
test("test")
test("test", map[string]interface{}{"dd": "DD", "cc": "CC"})
func test(required string, optional ...map[string]interface{}) {
fmt.Println(required)
fmt.Println(len(optional))
if len(optional) > 0 {
if val, existed := optional[0]["dd"]; existed {
fmt.Println(val)
}
}
}
將 slice params 傳入 args…
func echo(strings ...string) {
for _, s := range strings {
fmt.Println(s)
}
}
func main() {
strings := []string{"a", "b", "c"}
echo(strings...) // Treat input to function as variadic
}
interface & struct
Behaviours
scenario by value by reference
pointer method yes yes
value method yes yes
interface pointer method no yes
interface value method yes yes
ref:
[struct] 成員大小寫
跟 func 一樣,大寫代表 public,小寫是 private
[struct] map
type AWS struct {
SQS map[string]*aws_sqs.SQS
}
var qq AWS
qq.SQS = make(map[string]*aws_sqs.SQS)
qq.SQS[xx] = &ws_sqs.SQS{ ... }
[struct] function pointer
type Job struct {
Done func() error
}
var job Job
job.Done = done
func done() error {
}
[struct] 定義 + 賦值
resp := Music{
Genre: struct {
Country string
Rock string
}{
Country: "Taylor Swift",
Rock: "Aimee",
},
}
Assign a struct to an interface
type Interface interface{}
type Struct struct{}
func main() {
var ps *Struct
var pi *Interface
pi = new(Interface)
*pi = ps
_, _ = pi, ps
}
關於 interface 及 struct 的用法
package main
import "fmt"
# 用 interface 定義一個抽象層,只負責說這個類別有什麼動作
type PeopleAction interface {
AnimalAction
Stand()
}
# 本身也可以被繼承
type AnimalAction interface {
Run()
Eat()
}
# 定義資料結構
type Animal struct {
Name string
}
func (animal *Animal) Run() {
fmt.Println(animal.Name + " is running")
}
func (animal *Animal) Eat() {
fmt.Println(animal.Name + " is eatting")
}
func (animal *Animal) Stand() {
fmt.Println(animal.Name + " is standing")
}
func main() {
var jack PeopleAction = &Animal{"Jack"}
var bob AnimalAction = &Animal{"Bob"}
jack.Run() // Jack is running
jack.Eat() // Jack is eatting
jack.Stand() // Jack is standing
bob.Run() // Bob is running
bob.Eat() // Bob is eatting
// bob.Stand() // bob.Stand() Error : bob.Stand undefined (type AnimalAction has no field or method Stand)
}
Receive interface as param
type A interface{}
type B struct {
A
Age int
}
func main() {
var a A
fmt.Println("interface A type: ", reflect.TypeOf(a))
b := B{a, 30}
fmt.Println("struct B type: ", reflect.TypeOf(b))
Show(b)
}
func Show(a A) {
fmt.Println("show age: ", a.(B).Age)
fmt.Println("interface param type: ", reflect.TypeOf(a.(B)))
}
Result
interface A type: <nil>
struct B type: main.B
show age: 30
interface param type: main.B
Type T satisfies interface I defined in the first snippet. Values of type T can be f.ex. passed to any function accepting I as a parameter
type I interface {
M() string
}
type T struct {
name string
}
func (t T) M() string {
return t.name
}
func Hello(i I) {
fmt.Printf("Hi, my name is %s\n", i.M())
}
func main() {
Hello(T{name: "Michał"}) // "Hi, my name is Michał"
}
single type can implement many interfaces
type I1 interface {
M1()
}
type I2 interface {
M2()
}
type T struct{}
func (T) M1() { fmt.Println("T.M1") }
func (T) M2() { fmt.Println("T.M2") }
func f1(i I1) { i.M1() }
func f2(i I2) { i.M2() }
func main() {
t := T{}
f1(t) // "T.M1"
f2(t) // "T.M2"
}
different structs, call same name but different func
type I interface {
M() string
}
type T1 struct{ Name string }
type T2 struct{ Name string }
func main() {
// Different structs call its own func
var i I
t1 := T1{Name: "t1"}
t2 := T2{Name: "t2"}
i = &t1 // Must use pointer
fmt.Println(i.M())
i = &t2
fmt.Println(i.M())
}
func (t *T1) M() string { return t.Name + " T1" }
func (t *T2) M() string { return t.Name + " T2" }
same interface, different structs, call same func
No, you can’t!
Difference between passing by reference and passing by value
var a = &A{Val: "aaa"}
fmt.Println("a: ", a)
fmt.Println("b = a")
b := a
b.Val = "bbb"
fmt.Println("b: ", b, " (set new value to b)")
fmt.Println("a: ", a, " (affected by b)")
var c = &A{Val: "ccc"}
fmt.Println("c: ", c)
fmt.Println("b = c")
b = c
b.Val = "bbbbbbbbbbb"
fmt.Println("b: ", b, " (set new value to b)")
fmt.Println("c: ", c, " (affected by b)")
fmt.Println("a: ", a, " (unaffected)")
Result:
a: &{aaa}
b = a
b: &{bbb} (set new value to b)
a: &{bbb} (affected by b)
c: &{ccc}
b = c
b: &{bbbbbbbbbbb} (set new value to b)
c: &{bbbbbbbbbbb} (affected by b)
a: &{bbb} (unaffected)
struct + json
json.Marshal(struct {
Data *MyStruct `json:"data,omitempty"`
Status string `json:"status,omitempty"`
Reason string `json:"reason,omitempty"`
}{
Data: &r.Data,
Status: r.Status,
Reason: r.Reason,
})
}
Assign func
type Job struct {
ID string
Do func(*Job)
}
func main() {
var j1 = &Job{ID: "J01", Do: do}
var j2 = &Job{ID: "J02", Do: do}
j1.Do(j1)
j2.Do(j2)
}
func do(j *Job) {
fmt.Println("Job ID:", j.ID)
}
巢狀
type Resp struct {
UserID struct {
} `json:"user_id"`
Data struct {
Info struct {
Name string `json:"name"`
Friends []Friend `json:"friends"`
} `json:"info"`
} `json:"data"`
}
輸出
"user_id": {},
"data": {
"info": {
"name": "XXX",
"friends": [
{ },
{ },
],
},
},
傳遞參數不像 map 及 slice 以指標傳遞, 而是傳遞實體
func main() {
q := qq{name: "ori"}
fmt.Println(q)
dd(q)
fmt.Println(q)
cc(&q)
fmt.Println(q)
}
func dd(q qq) {
q.name = "xx"
}
func cc(q *qq) {
q.name = "zz"
}
result
{ori}
{ori}
{zz}
JSON tag 補充
// 接收跟輸出都會忽略
Field int `json:"-"`
// Field appears in JSON as key "myName" and the field is omitted from the object if its value is empty, as defined above.
Field int `json:"myName,omitempty"`
// Field appears in JSON as key "Field" (the default), but the field is skipped if empty.
Field int `json:",omitempty"`
type Ticket struct {
Places []Place `json:"places"` // json 傳過來時的 places 也要是陣列
Name string `json:"name"` // 單純接收字串
UID string // 注意! 即使沒設定 tag, 如果傳進來是 "uid": "123" 這樣也會 map 得到
FriendID string // 如果傳進來是 "friend_id": "123" 這樣 map 不到
}
Nested struct 即使有設 omitempty 也 json 仍會輸出 "field": {}
, 解決方法是指定 pointer
給 struct
type A struct { Val L2 `json:"val,omitempty"` }
type B struct { Val *L2 `json:"val,omitempty"` } // pointer 是關鍵
type L2 struct { Val string `json:"val,omitempty"` }
json.Marshal
輸出 :
a := &A{Val: L2{Val: ""}} // {"val":{}}
a := &A{} // {"val":{}}
a := &B{} // {}
避免空的 slice json marshal 後為 null
var users []map[string]interface{}
if users == nil {
users = make([]map[string]interface{}, 0)
}
import
運作流程
當 import A 時, 會先執行 A 的 init(), 但如果 A 裡面還有 import B, 則會優先執行 B 的 init(),
當 package 的 init() 都執行完後, 才會執行 main 的 init(), 最後才會執行 main()
import 的幾種類型
-
正常引入
import(
“fmt”
)
這是我們平常最常引入的方法
-
省略前綴的 package name
import (
. “fmt”
)
ex : fmt.Println("hello world")
可以省略寫成 Println("hello world")
-
Alias
import (
f “fmt”
)
ex : f.Println("hello world")
-
不引用 package 內部函數
import (
_ “fmt”
)
當引入這個 package 只會執行它的 init()
套件管理
dep (官方)
dep init
: 第一次才會用到, 會產生 Gopkg.toml, Gopkg.lock (不要理它), vendor (安裝好 package)
dep ensure
: install, Gopkg.toml 裡面設定的 package. 另外如果要新增套件到 Gopkg.toml 也是用這個指令, 不用指定套件它會自已去掃
dep ensure -update
: 升級所有套件到最新的
dep ensure -add github/aaa/bbb@{tag or commit_id}
: 新增套件到 Gopkg.toml / Gopkg.lock and vendor
dep status
: 顯示你目前的版本跟最新的版本
- 升級某個套件版本要改 Gopkg.toml, 再執行 dep ensure
Vendor
govendor,用起來挺直覺的,如果使用過 npm 或 gem 的經驗,它的概念是一樣的
govendor cheat sheet
第一次使用
govendor init # 會產生 vendor/vendor.json, 但裡面是空的
govendor add +external # 幫你把目前使用到 GOPATH 套件版本填到 vendor.json,也會產生 github.com, etc. 第三方套件
第 N 次使用, 更新 govendor.json
govendor remove +v # 清空 `vendor.json` 及刪除 `github.com`, .. etc.
govendor add +external # 再重加有使用到的套件
依據 vendor/vendor.json
使用到的套件從 local 的 GOPATH
加到 vendor/
, 如果 local 沒有要先下 go get
govendor sync
如果 github 某個套件更新了, 想更新 vendor 這個套件
govendor fetch golang.org/x/net/context
Error: Remotes failed for
如果有個 local repo (private repo) 加不進 vendor/
下
$ govendor sync
Error: Remotes failed for:
Failed for "relog" (failed to ping remote repo): unrecognized import path "relog"
直接 add
那個 local repo
govendor add relog
升級某一套件版本
govendor fetch github.com/satori/go.uuid
(不要採用, 僅保留)
- 先從 govendor 移除掉
govendor remove github.com/parnurzeal/gorequest
- 移除 local 的
github.com/parnurzeal/gorequest
go get github.com/parnurzeal/gorequest
govendor add +external
更新到 vendor.json
不管怎麼重 build, code 一直是舊的
有可能你將目前專案加到 vendor/
下了, 把它刪掉, 再重編就可以了
Panic and Recover
You can only use recover()
to catch panic. You can’t catch fmt.Fatal("something went wrong")
.
攔截並印出 error
如果不想因為程式發生 index out of range
或由 panic 擲出的 error 導致整個程序強制中止的話可以用 recover 來攔截,
另外有時候程式的 error message 不夠明確時, 用 recover 有時候可以得到較明確的訊息
defer func() {
if e := recover(); e != nil {
// e is the interface{} typed-value we passed to panic()
fmt.Println("Whoops: ", e) // Prints "Whoops: boom!"
}
}()
var qq []string
fmt.Println(qq[0]) // 或直接用 panic("boom!")
fmt.Println("This will never be called")
recover()
要寫在最前面,否則會無法攔截到
將 stack 裡的東西印出來
func main() {
defer func() {
if e := recover(); e != nil {
for i := 1; ; i++ {
_, file, line, ok := runtime.Caller(i)
if !ok {
break
}
log.Println(fmt.Sprintf("%s:%d", file, line))
}
}
}()
qq() // :21
log.Println("Done!")
}
func qq() {
ff() // :26
}
func ff() {
panic("ff") // :30
}
Result:
2017/09/20 19:16:22 /usr/local/go/src/runtime/asm_amd64.s:514
2017/09/20 19:16:22 /usr/local/go/src/runtime/panic.go:489
2017/09/20 19:16:22 /tmp/qq.go:30
2017/09/20 19:16:22 /tmp/qq.go:26
2017/09/20 19:16:22 /tmp/qq.go:21
2017/09/20 19:16:22 /usr/local/go/src/runtime/proc.go:185
2017/09/20 19:16:22 /usr/local/go/src/runtime/asm_amd64.s:2197
其他
tag
type User struct {
_ struct{} `type:"structure"`
name string `json:"name-field"`
age int
}
func main() {
user := &User{name: "John Doe The Fourth", age: 20}
if field, ok := reflect.TypeOf(user).Elem().FieldByName("_"); ok {
fmt.Println(field.Tag)
} else {
panic("Field not found")
}
if field, ok := reflect.TypeOf(user).Elem().FieldByName("name"); ok {
fmt.Println(field.Tag)
} else {
panic("Field not found")
}
}
結果:
type:"structure"
json:"name-field"
runtime
- The runtime library implements garbage collection, concurrency, stack management, and other critical features of the Go language.
- The Go runtime manages scheduling, garbage collection, and the runtime environment for goroutines.
Pointer guildeline
- struct call func by value 會複製自已一份, 如果 struct 很大的話用 pointer
- 如果 func 裡面需要改變本身 struct 的值的話用 pointer
- 有些 func 雖然不需要用 pointer, 但為了一致性還是使用 pointer 較好
- Slices, maps and channels are reference types that do not require the extra indirection of an allocation with new
- If the receiver is large, a big struct for instance, it will be much cheaper to use a pointer receiver.
- If some of the methods of the type must have pointer receivers, the rest should too, so the method set is consistent regardless of how the type is used.
- For types such as basic types, slices, and small structs, a value receiver is very cheap so unless the semantics of the method requires a pointer, a value receiver is efficient and clear.
盡量減少在其他子 package 用 var 定義全域變數
多使用全域來定義 struct
, func
, const
,不得以才用 var 定義全域變數;如果有共用的 func 都會用到共同的變數,就用 struct 定義,否則容易產生 Memory Leak 的問題
go env
$ go env
GOARCH="amd64"
GOBIN=""
GOCHAR="6"
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOOS="darwin"
GOPATH=""
GORACE=""
GOROOT="/usr/local/go"
GOTOOLDIR="/usr/local/go/pkg/tool/darwin_amd64"
CC="gcc"
GOGCCFLAGS="-g -O2 -fPIC -m64 -pthread -fno-common"
CGO_ENABLED="1"
show variable type
import "reflect"
fmt.Println(reflect.TypeOf(tst))
判斷 interface{} 是否是 slice
if reflect.TypeOf(test["slice"]).Kind() == reflect.Slice {
}
debug
如同 PHP 的 print_r()
, var_dump()
, var_export()
, 如果型態是 map 它也會把 key value 印出來
fmt.Printf("%v", whatever)
用上面的不會 struct 的欄位名稱,如果印出 struct 的欄位及值要用 :
fmt.Printf("%+v", whatever)
string literals are character sequences
string("Hello"[1]) // ASCII only e
string([]rune("Hello, 世界")[1]) // UTF-8 e
string([]rune("Hello, 世界")[8]) // UTF-8 界
指令
- syntax checking :
gofmt -e xx.go
- 透過傳遞參數的方式改變 main.go 裡面 var 變數 :
go build -ldflags '-X main.BUILD_ID=1'
- 當
go build
時太多 error 時你只會看到幾行然候其他就被省略成 too many errors
, 改用 go build -gcflags="-e"
就可印出全部的 error
在 if 裡面 new 參數無法傳遞到外面
裡面有 new
var a bool
if true {
a := true
fmt.Println(a)
}
fmt.Println(a)
true
false
裡面沒 new
var a bool
if true {
a = true
fmt.Println(a)
}
fmt.Println(a)
true
true
MySQL NULL
null
string sql.NullString
int64 sql.NullInt64
float64 sql.NullFloat64
time 加上 tag
time.Time `sql:"default:null"`
或是用指標
string *string
for range 下在傳遞指標(pointer)需要注意
ii := []int{1, 2, 3}
dd := map[int]*int{}
dd2 := map[int]*int{}
for k, i := range ii { // 不要使用 for range new 出來的 value, 因為最後都會指向同一個位置
dd[k] = &i
}
for k, _ := range ii { // 明確指定記憶體位址
dd2[k] = &ii[k]
}
fmt.Println(dd) // map[0:0x41602c 1:0x41602c 2:0x41602c]
fmt.Println(dd2) // map[0:0x416020 1:0x416024 2:0x416028]
go mod
finding module for package
go mod tidy
go: finding module for package github.com/user_name/private_repo_name/package_name
test/controller imports
github.com/user_name/private_repo_name/package_name: cannot find module providing package github.com/user_name/private_repo_name/package_name: module
github.com/user_name/private_repo_name/package_name: git ls-remote -q origin in /Users/jacklin/go/pkg/mod/cache/vcs/697da452d33316e5203c4e98f8eb82049f3cb1b48b123194bc15
8a0a4d78af7e: exit status 128:
fatal: could not read Username for 'https://github.com': terminal prompts disabled
Confirm the import path was entered correctly.
If this is a private repository, see https://golang.org/doc/faq#git_https for additional information.
The reason why these issues exist is using go module and GOPATH at the same time.
Move all go projects out of GOPATH/src
, use go module only
package ... is not in GOROOT
It might be caused by importing another module in the same file systems
You need to tell go modules where to file that module
go mod edit -replace example.com/greetings=../greetings
go mod tidy
go get private repo
Add
go env -w GOPRIVATE=github.com/user_name/pkg_name
.gitconfig
[url "https://user_name:{ACCESS TOKEN}@github.com"]
insteadOf = https://github.com