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 %v verb 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

4delete
slice1 := []string{"","","",""}
newSlice := append(slice1[:2],slice2[3:]...)

5copy
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 the deferError method.
    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 value b will have a type *Student and a nil value. 

 

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

 

posted @ 2023-10-13 22:02  PEAR2020  阅读(17)  评论(0)    收藏  举报