defer

记住两个性质: 1、同栈,后进先出 2、在推迟执行时已经求值,不是在调用时才求职。 (官网例子说明)

Effective Go学习_后进先出Effective Go学习_初始化_02
func trace(s string) string {
    fmt.Println("entering:", s)
return s
}

func un(s string) {
    fmt.Println("leaving:", s)
}

func a() {
defer un(trace("a"))
    fmt.Println("in a")
}

func b() {
defer un(trace("b"))
    fmt.Println("in b")
a()
}

func main() {
    b()
}
View Code

 

 

Effective Go学习_后进先出Effective Go学习_初始化_02
input result

entering: b
in b
entering: a
in a
leaving: a
leaving: b
View Code

 

内存分配

Go与C不同,该局部变量对应的数据在函数返回后依然有效。实际上,每当获取一个复合字面的地址时,都将为一个新的实例分配内存。
  new 分配内存,不会初始化内存,只会将内存置零,返回指针
  make 分配内存,已初始化,用于创建slice,map,channel,不返回指针

打印格式

fmt.Printf. %v --> value, 任意值,甚至是array,struct, map
       %+v --> 为结构体的每个字段加上字段名
     %#v --> 完全按照Go的语法打印值
     %T --> 打印值的类型

...用法

...形参可以指定具体类型,代表这个类型元素还有很多
func append(slice [] T, element ...T)
append(slice, element1,elment2)
append(slice, slice2...)

值方法和指针方法

如果实现了接收者是值类型的方法,会隐含地也实现了接收者是指针类型的方法。
如果方法的接收者是值类型,无论调用者是对象还是对象指针,修改的都是对象的副本,不影响调用者;如果方法的接收者是指针类型,则调用者修改的是指针指向的对象本身。
使用指针作为方法的接收者的理由:
  方法能够修改接收者指向的值。
  避免在每次调用方法时复制该值,在值的类型为大型结构体时,这样做会更加高效。
如果类型具备“原始的本质”,也就是说它的成员都是由 Go 语言里内置的原始类型,如字符串,整型值等,那就定义值接收者类型的方法。像内置的引用类型,如 slice,map,interface,channel,这些类型比较特殊,声明他们的时候,实际上是创建了一个 header, 对于他们也是直接定义值接收者类型的方法。这样,调用函数时,是直接 copy 了这些类型的 header,而 header 本身就是为复制设计的。
如果类型具备非原始的本质,不能被安全地复制,这种类型总是应该被共享,那就定义指针接收者的方法。比如 go 源码里的文件结构体(struct File)就不应该被复制,应该只有一份实体。
这一段说的比较绕,大家可以去看《Go 语言实战》5.3 那一节。参考: https://qcrao91.gitbook.io/go/interface/zhi-jie-shou-zhe-he-zhi-zhen-jie-shou-zhe-de-qu-bie

协程

Effective Go学习_后进先出Effective Go学习_初始化_02
func Serve(queue chan *Request) {
    for req := range queue {
    sem <- 1
    go func() {
    process(req) // Buggy; see explanation below.
    <-sem
    }()
    }
}

func Serve(queue chan *Request) {
    for req := range queue {
    sem <- 1
    go func(req *Request) {
    process(req)
    <-sem
    }(req)
    }
}
View Code

 

req在每次for循环的go协程中被共享,不能保证在每个协程是唯一的,所以要做协程参数。 --->闭包概念

——世界上从来不乏优秀的人,我只是想接近他们一点。