go 编程语法
variable & const
eclipse keymap
alt + -> = step into
alt + <- = step out
ctrl + d = delete
a := 1 cannot be used to declare global variable
one package one main(){}
CONST can only define bool,number,string
const( x = 16 y ) => y = 16 iota special constant variable const( err1 = iota err2 err3 ) 中断需要显式的恢复iota,后续自动递增 再次出现
const( err4 = iota ) err4 归0

匿名变量:
_,ok = function()
basic data type
1.type
bool
num{
int8 有符号
int16
int32
int64
int//dynamic
uint16 >= 0 无符号
float32
float64
<no float>
}
string{
byte = uint8 <太小> 0~255 如果255+1,wrap around to 0
<alias>
< = char>
rune = int32 <大>
}
var c byte
c = 'a'
fmt.Print(c) //97
fmt.Printf("%c",c) //a
var c2 rune
c2 = '中'
fmt.Printf("%c",c2) //中
2. conversion of type
var a int = 12
type IT int
// 定义一个别名
var c IT = IT(a)
// strict type requirement
var a int8 = 12
var b = uint8(a)
strconv: str to _
strconv.Atoi()
strconv.Itoa()
`
var str = "12"
atoi, err := strconv.Atoi(str)
if err != nil {
fmt.Println("convert error")
}
fmt.Println(atoi)
`
strconv.ParseInt("12",base 8, bitsize 64)
=> 1 * 8 + 2 * 1 = 10
hadle exception when str to number/bool
3. format (to str)
strconv.FormatBool(true)
strconv.FormatFloat(3.14159,'f',-1,64)
4.
var a = 60
c := &a // variable address
var c *int // pointer type
str
len(Type)
name := "imooc"
bytes := []byte(name)
// string to slice
one chinese character: 3 bytes && 1 rune
`
name := "imooc一二三四五"
fmt.Println(len([]rune(name)))
` // 10
`
name := "imooc一二三四五"
fmt.Println(len([]byte(name)))
` // 20
转义符: “”中加上 \
但使用``,不需要转义符
fmt.Printf("%s %d ",username,age)
// poor performance
as a string:
msg := fmt.Sprintf("%s %d ",username,age)
- %v : Description: The
%vverb prints the value in a default format. - %#v: detailed format, for debugging or logging
- %T : type of a value
- The
%t: a boolean value.
var bdr strings.Builder
bdr.WriteString("xxx")
str = bdr.String()
// comparison
a := "hello"
b := "hello"
fmt.Println(a == b)
n Go, the == operator for strings compares the contents (i.e., the sequence of characters) of the strings, not their memory addresses.
import "strings"
- strings.Contains(,)
- strings.Count(,)
- strings.Split(,"-")
- strings.HasPrefix(,"prefix")
- strings.HasSuffix(,"prefix")
- strings.Index()
- strings.Replace(name,"old","new",count)
- strings.ToLower("GO") // ToUpper()
- strings.Trim("#123#$", "#$")
- strings.TrimLeft()
for-loop
for i:= 0; i<3 ;i++{
}
var i int
for{
time.Sleep(2*time.Second)
i++
}
for range 针对字符串,数组,切片,map,channel
for index,value := range str{
// 可以输出中文
}
for _,value := range str{
// value是字符的拷贝
}
for index := range str/array/slice{
}
for _,value := range map{
}
for data := range channel{
// data that channel received!!!
}
goto // 用于错误处理,挑战到统一标签一起处理
goto over
over:
fmt.println("here")
swtich var1{
case v2:
...
default:
...
}
or
swtich {
case age == 1:
...
case name == "me":
...
default:
...
}
array & slice
1. array
var courses [3]string
couses1 := [3]string{"1","2","3"}
couses1 := [3]string{2:"3"}
couses1 := [...]string{"1","2","3"} // 无需声明长度
same length, same type => array can compare!!!!
arrays can use == to identify if identical
multi-dimension array
var m = [3][4]string
m[0] = [4]string{"1","2"}
for _,row := range m{
for _,col := range row{
fmt.print(col)
}
fmt.println()
}
2. slice 动态数组
slice cannot be compared!!!!
var courses []string
// special method,输入输出都切片
courses = append(courses,e1,e2,e3)
1》 init 3 ways: 1)from array 2)string{} 3)make
books := [...]string{"1","2","3"} bookSlice := books[0:1] // [) bookSlice2 := books[0:len(books)] // [) books := []string{"1","2","3"} // allocate size虽然非必要,但对性能要求高的时候优先使用, Pre-allocating a slice with sufficient capacity can improve performance by reducing the number of memory allocations as the slice grows. books := make([]string,size) // 初始化后append只会添加到后面 books[0] = "c" 2》 bookSlice := books[1:] // to end bookSlice := books[:2] // from start bookSlice := books[:] // all 3》 slice1 := []string{"",""} slice2 := []string{"",""} slice1 = append(slice1,slice2[1:]...) // ... can convert slice to string 4》delete slice1 := []string{"","","",""} newSlice := append(slice1[:2],slice2[3:]...) 5》 copy slice2 := slice1 slice1 := []string{"1", "2", "3", "4"} newSlice := make([]string, len(slice1)) copy(newSlice, slice1) // 浅度拷贝 fmt.Println(newSlice)
6》slice在参数传递时是值传递or引用:值传递,引用的效果
func printSlice(slice []string) {
slice[0] = "0"
for i := 0; i <= 10; i++ {
slice = append(slice, strconv.Itoa(i))
}
}
func main() {
slice1 := []string{"1", "2", "3"}
printSlice(slice1)
fmt.Println(slice1)
}
现象:can change like reference
but cannot append, should be 值传递
原理:以下为originslice 和 进入参数的 slice,以下是扩容前:capacity expansion

随着append增加elements,会成倍扩容:
pointer为开始,数len个长度,cap是pointer到结尾

所以扩容前是引用传递的效果,扩容后是值传递,所以append有风险导致变动后的slice没有变动
所以需要receive the result courses = append(courses,"ele1","ele2")
At the beginning, the expansion was doubled, but then the expansion started to slow down
map
1. init
var courseMap = map[string]string{
"go":"go engineer",
"grpc" : "gggg",
}
courseMap["go"] = "123"
var courseMap = map[string]string
// nil
courseMap["go"] = "123"
// error!
//make() used to initialize slice,map,channel
var courseMap = make(map[string]string,3) courseMap["go"] = "123"
In a nutshell, map initialize:
1. map[string]string{"":"","":"",}
2. make(map[string]string,3)
3. slice can not be initialized, it can []string and then append(,)
2. iterate
for key,value := range courseMap{
fmt.Println(key,value)
}
3. unordered, and not guarantee sequence is identical for every iteration
4.if contains key
var courseMap = map[string]string{
"go": "go1",
"to": "to1",
}
m, ok := courseMap["java"]
if ok {
fmt.Println(m)
} else {
fmt.Println("not exist")
}
// to simplify and be more readable
if _, ok := courseMap["java"]; ok{
}
5. delete
delete(courseMap,"grpc")
! will not pop an error if delete an non-existing key
6. map is not Thread safe
instead, use => sync.Map
if the map value type is a struct, changes to fields directly through the map won't persist because the map holds a copy of the struct. Using a pointer allows modifications to the actual struct.

package main import "fmt" type Student struct { name string } func main() { m := map[string]*Student{"people": &Student{"zhoujielun"}} m["people"].name = "wuyanzu" fmt.Println(m["people"].name) // Output: wuyanzu }
sync.Map
use Range() to iterate and get the size
package main import ( "fmt" "sync" ) func main() { var m sync.Map // Add some entries to the map m.Store("key1", "value1") m.Store("key2", "value2") m.Store("key3", "value3") // Count the number of entries count := 0 m.Range(func(key, value interface{}) bool { count++ return true // continue iterating }) fmt.Printf("Number of entries: %d\n", count) }
while map use len() to get the size
list (linkedlist)
Waste of space/uncontinuous space/ Big performance difference
一般使用slice/map
`
import "container/list"
//1.
var mylist list.List
//2.
mylist := list.New()
mylist.PushBack("go2")
mylist.PushBack("grpc2")
mylist.PushFront("go1")
mylist.PushFront("grpc1")
`
print:for-loop
for i:= mylist.Front(); i!=nil; i = i.Next(){
// i.Value (any -> interface)
}
for i:= mylist.Back(); i!=nil; i = i.Pre(){
}
mylist := list.New() mylist.PushBack(1) mylist.PushBack(2) mylist.PushBack(3) mylist.PushBack(4) i := mylist.Front() for ; i != nil; i = i.Next() { if i.Next().Value.(int) == 4 { break } } // mylist.InsertAfter(3.5, i) mylist.Remove(i) for j := mylist.Front(); j != nil; j = j.Next() { fmt.Println(j.Value) }
function(anonymous/clousure)
go函数支持普通函数,匿名函数,闭包
1.函数本身作变量
2.匿名函数 闭包
3.函数可以满足接口
func add(a,b int) (int,error){
return a+b,nil
}
go语言中全部是值传递
func add(a ...int) (sum int,err error){
// 用省略号传进来的是slice,for来拿
for _,value := range a{
sum += value
}
return sum,nil
}
1. as an variable
funcvar := add // 不可以加括号否则变成了调用!
2. as an return/input value
func cal(str string, items ...int) func(){
switch str{
case "+":
return func(){
// +
}
case "-":
return func(){
// -
}
}
}
cal("+") // function
cal("+")() // invoke function
// 传递代码through input
func cal(num int, myfunc func(items ...int) int) int{
return int + myfunc(); // invoke the input function
}
// 临时传的函数没有名字,匿名函数
cal(1,func (items ...int) int{
// code
})
clousure:一个函数中访问另一个函数的局部变量
func autoIncrement() int{
local := 0
return func() int{
return local++
}() // 返回函数的调用
}
for i:=1;i<=10;i++{
fmt.Println(autoIncrement())
}
这样达不到auto_increment目的
=>
func autoIncrement() func() int {
local := 0
return func() int {
local++
return local
} // 返回一个函数,没有调用
}
func main() {
nextFunc := autoIncrement()
for i := 1; i <= 5; i++ {
fmt.Println(nextFunc())
}
nextFunc := autoIncrement()
// return to 0 again
}
defer/panic/recovery
defer:execute before return
USED TO CLOSE DATABASE/FILE/UNLOCK
1. why we use defer?
var mu sync.Mutex
mu.Lock()
defer mu.unLock()
...
2. sequence of defer : stack
defer fmt.println("1")
defer fmt.println("2")
fmt.println("main")
3. when used in function
func deferReturn() (ret int){
defer func(){
ret ++
}()
return 10
}
ret := deferReturn()
// ret == 11
2. ERROR HANDLER
error,panic,recover
return err to tell if success
go设计者认为必须要处理这个error,防御编程
func A() (int, error){
return 0, error.New("this is an error")
}
func main(){
if _, err := A(); err != nil{
fmt.Println(err)
}
}
panic(): quit the program and print the error stack
USED WHEN some dependencies must exist / service must be availbale / mysql connection is ready before service starts
recover can catch panic
// defer needs to be defined before panic!! // recover should be in the defer func() // recover 还是相当于停止执行了,并不会接下去执行 // multi-defer is like a stack defer func(){ if r := recover(); r != nil{ // catch the panic and turn into error log } }() var names map[string]string names["go"] = "123" // trigger panic
recover
哪里panic,哪里recover
type Project struct{} func (p *Project) deferError() { if err := recover(); err != nil { fmt.Println("recover: ", err) } } func (p *Project) exec(msgchan chan interface{}) { defer p.deferError() // ensuring that any panic occurring inside this method is recovered by thedeferErrormethod. for msg := range msgchan { m := msg.(int) fmt.Println("msg: ", m) } } func (p *Project) run(msgchan chan interface{}) {
// cannot put deferError here, otherwise, panic inside exec will still cause abnormal close, recover fail for { go p.exec(msgchan) time.Sleep(time.Second * 2) } } func (p *Project) Main() { a := make(chan interface{}, 100) go p.run(a) go func() { for { a <- "1" time.Sleep(time.Second) } }() time.Sleep(time.Second * 5) } func main() { p := new(Project) p.Main() }
struct
(1)type
1. define structure
2. define interface
3. define type alias for better readability
type MyInt = int
var i MyInt // point to int
i+j //
4. define a new type
type MyInt int
var i MyInt // MyInt and int is different
var j int = 8
int(i) + j // requires cast the type
USED WHEN int类型+扩展方法
type MyInt int
// function with no name, binded to the customized type
func (m MyInt) toString() string{
return strconv.Itoa(int(m))
}
// invoke
var a MyInt = 123
a.toString()
5. judge the type
// get the actual type
var a interface{} = "abc"
switch a.(type){
case string:
}
// assertion (???)
m := a.(string)
(2) struct
type Person struc{
name string
age int
}
2.1 init
p1 := Person{"boby",18}
// can omit
p1 := Person{
name: "boby",
}
var persons []Person
persons = append(persons,Person{
name:"bobby"
})
persons2 := []Person{
{
age:12,
},
{
},
{
},
}
2.2 anponymous struct 边定义边实例化
address := struct{ province string city string }{ "123", "123", } group := []struct{ province string city string }{ {"",""}, {"",""}, }
2.3 nest
type Person struct{ name string age int } type Student struct{ p Person // 1> nest score float32 // 2> or anonymous nest Person age int } // 1st & 2nd all need this init s := Student{ Person{ "bobby",11 }, 95.3, } // access is different // 1st nest s.p.age // 2nd nest s.age // set value s.name => set the external attr!!!
2.4 method
// receiver:绑定有两种形态
func (p Person) print1(){
p.age = 19 // will not change the age! 因为是值传递
fmt.Println()
}
// when we need to change the p, or OBJECT Person is too big that costs performance
// space-efficient
func (p *Person) print2(){
p.age = 19 // change to 19,*表示引用传递,pointer
fmt.Println()
}
s.print() // nest also work
// 值类型可以调用指针接收器的方法
p := Person{
"bobby",19,
}
p.print2()
// 指针可以调用值类型接收器的方法
p := &Person{
"bobby",19,
}
p.print1()
// print1 & print2 不能同名
pointer
&p 取地址, 传入 *person指针类型

fmt.Printf("%p",&p). // p是十六进制
type Person struct { name string } func changeName(p *Person) { p.name = "changeddddd" } func main() { var p = Person{"mynname"} changeName(&p) fmt.Println(p.name) }
1.变量声明为指针类型*Person (nil)
通过&Person{}初始化值
2. 取值
(*po).name // 指针*+变量是拿到变量真正的值
po.name // also can
3. 指针不能参加运算
3 ways to init: 下面的a和b都表示地址
* 表示地址表示的值;&表示值所在的地址
a := &Person{} var a Person = 10 ??? b := &a a := new(Person)
初始化:map,channel,slice使用make()
pointer使用new()
map必须初始化
swap
//swap
func swap(a,b *int){
a,b = b,a // 只是指向了不同的地址,没有影响本身的地址
}
a,b := 1,2
swap(&a,&b) // still 1,2

func swap(a,b *int){ t := *a // 获得本身的地址,改变地址的值 *a = *b *b = t }
// 2,1
nil
string: ""
*string: nil
channel/interface/function/slice default: nil
struct的default value != nil,是具体字段的默认值
struct可以用 ==
p1 := Person{
name : "b1",
age: 19,
}
p2 := Person{
name : "b1",
age: 11,
}
if p1 == p2{
}
var s1 []Person // nil slice
var s2 = make([]Person,0) // empty slice
解释:slice本质是struct,*element指向一个数组。初始化后len=0,cap=0,*ele =nil所以整体不能算nil

interface
1. define interface
ducking typing type Duck interface{ Gaga() Walk() Swimming() } // implement Duck but not bind it // helps to decouple type pskDuck struct{ legs int } func (pd *pskDuck) Gaga(){ // "pskDuck gaga" } func (pd *pskDuck) Walk(){} func (pd *pskDuck) Swimming(){} var d Duck = &pskDuck{} // must implement all methods d.Gaga() //type any interface{}
如果是实现了receiver为pointer的方法: var d Duck = &pskDuck{}
如果是实现了receiver为value type的方法: var d Duck = pskDuck{}

an interface that implements a `nil`struct is not nil
In Go, an interface with a nil underlying value is still considered a non-nil interface.
This is because the type of the interface is not nil; it retains the type information even if the underlying value is nil.
package main import ( "fmt" ) type People interface { Show() } type Student struct{} func (stu *Student) Show() { } func live() People { var stu *Student return stu } func main() { b := live() if b == nil { fmt.Println("AAAAAAA") } else { fmt.Println("BBBBBBB") } }
// The resulting interface valuebwill have a type*Studentand anilvalue.
2. multi interfaces - single implementation/or multi 多对多
type MyWriter interface{ Write(string) error } type MyCloser interface{ Close() error } type writerCLoser struct{} func (wc *writerCLoser) Write(str string) error{ fmt.Println("writing...",str) return nil } func (wc *writerCLoser) Close() error{ fmt.Println("closing...") return nil } var mw MyWriter = &writerCLoser{} var mc MyCloser = &writerCLoser{} mw.Write(strconv.Itoa(123)) mc.Close()
实现类注入一个实现类
type Write interface { DoIt() } type MyWriter struct { Write // 不写变量名,表示是interface注入 s string } type DataBaseWriter struct{} type FileWriter struct{} func (d *DataBaseWriter) DoIt() { fmt.Println("database ...") } func (d *FileWriter) DoIt() { fmt.Println("file ...") } func main() { var w Write = &MyWriter{ &DataBaseWriter{}, "my", } //w.w.DoIt() w.DoIt() // database... }
assertion
func add(a,b interface{}) interface{}{ a1,_ := a.(int) // cast to int b1,_ := b.(int) return a1+b1 } func add(a,b interface{}) interface{}{ switch a.(type) { case int: a1,_ := a.(int) b1,_ := b.(int) return a1+b1 case float32: a1,_ := a.(float32) b1,_ := b.(float32) return a1+b1 default: panic("not supported type") } } // can be replaced by Generics 泛型
inferface nest interface
package nest type MyWrite interface { Write(string) } type MyReader interface { Read() string } type MyReadWriter interface { MyWrite MyReader ReadWrite() } type ImpleRW struct {} func (i ImpleRW) Write(s string) { //TODO implement me panic("implement me") } func (i ImpleRW) Read() string { //TODO implement me panic("implement me") } func (i ImpleRW) ReadWrite() { //TODO implement me panic("implement me") } func main() { var m MyReadWriter = &ImpleRW{} // 绑定的是值,func (i ImpleRW) ,既可以 &ImpleRW{} 也可以 ImpleRW{} // 绑定的是指针,func (i *ImpleRW),只能 &ImpleRW{} => 一般这么使用 m.Read() }
slice []interface{}的问题
package main import "fmt" func printErr(data ...interface{}) { for _, value := range data { fmt.Println(value) } } func main() { var i = []interface{}{ "miss", 1, 19, } printErr(i...) var s = []string{ "bob", "me", "borow", } // printErr(s...) must be interface{}, we can convert []string to []interface var is []interface{} for _, value := range s { is = append(is, value) } printErr(is...) }
error
Given the fact the error is an interface that has Error() string
we can implement the error
type testImpl struct{} func (t testImpl) Error() string { return "a???? " }
// use:
var err error = &testImpl{}
fmt.Println(err.Error()) // a???

浙公网安备 33010602011771号