Effective Go & GoFAQ 阅读笔记

Effective Go & GoFAQ 阅读笔记

Effective go: https://golang.org/doc/effective_go.html

Go FAQ: https://golang.org/doc/faq

下面是对以上两篇文章内相关内容的记录与解释/总结

Interface

某一个 type “implement” 这个 interface 的话,只要实现这个interface的全部方法即可 不过这里有一些容易产生误区的地方,看下面这个例子

 

type T int

interface Equaler {
    Equal(Equaler) bool
}

Equaler是一个interface, 下面看这两个例子

func (t T) Equal(u T) bool {
    return t == u
}

type T并没有实现Equaler interface 因为参数类型不是 Equaler 而是一个具体的类型 T

这个问题很容易理解, 试想一下其他某一个type implement了Equaler类型(假设为Q) 而 t.Equal(Q) 是不合法的 因为Q不是T类型, 所以 T 并没有实现Equaler这个interface 因为Equaler接口要求只要满足type为Equaler就都可以作为参数传递给Equal

下面这个才是T implement Equaler的例子

func (t T) Equal(u T) bool {
    return t == u.(T)
}

 

interface 的 value = nil 不代表interface为nil, interface的结构为type, value两部分 下面这个代码永远会返回错误

type DemoError struct {
    msg string
}

func (de DemoError) Error() string {
        return fmt.Sprintf("DemoError")
}

func main() {
        err := Process()
        if err != nil {
                fmt.Println("ERROR")
        }
}

func Process() error {
        var p *DemoError = nil
        if bad() {
                p = &DemoError{msg:"ERROR"}
        }
        return p
}

上述代码中 Process成功将返回一个p(type=*DemoError, value=nil)这个与nil进行对比的时候,永远会返回假 因而不要使用这种方式返回error相对的,直接return Error即可(待斟酌)

 

map slice channel存的是引用, array 存的是value

在go中函数传递的都为按值传递, 如果传递一个array并且对array进行修改,修改是无法保留下来的,这个和C语言中传递array不一样,C语言传递数组传递的是数组的地址,如果想要修改数组中的元素, 可以传递slice,并不是说slice的传递是按照地址传递,slice传递依旧是按值传递,不过slice包含三部分 len, cap, reference to array  而对slice操作的时候,操作的就是它下面指向的array,因而可以修改元素,不过没有办法append元素或者remove元素,这些操作会修改len 和 cap,在函数返回的时候len和cap的修改会丢失

Idiom

Almost Never, Do not use a pointer to an interface

Do not communicating by sharing memory, Share memory by communicating

Concurrency is not Parallelism

 

Naming

对于Getter的名字,不要使用GetXXX

interface type name end with -er

 

Control Flow

if often exit current function

e.g:

if statement {
    xxx
    return/break/continue
}

if 经常直接用于退出这个函数,执行块等,这样就可以省去多余的else

 

switch可以用来判断value的类型

用法为

switch t.(type) {
    case int:
    case int64:
    case string:
}

 

break 可以跳出多层循环

用法为 break Label

注意Label一定要打在某一个循环的for statement之前,位置错误的话会报编译错误

 

Function

 

使用named return value

使用这种形式

func ThisIsGood() (err error) {
    if bad() {
        err = errors.New("Bad")
        return
    }
    return
}

而不是这种

func ThisIsBad() error {
    if bad() {
        err := errors.New("Bad")
        return err
    }
    return nil
}

 

使用 defer 做cleanup

当打开文件, 数据库链接,或者TCP链接的时候,我们需要在使用完毕之后关闭链接, 在传统的C语言中, 在函数退出的地方编写cleanup相关的代码 而在go里, 有一个书写,阅读都更友好的方式,即使用defer

func DoSomething() (err error) {
    f, err := os.Open("/etc/passwd", os.O_RDONLY)
    if err != nil {
        err = errors.Wrap("Cannot open file")
    }
    defer f.Close()
    ...
}

 

这种写法的好处有两点(至少)

  1. 在打开的代码之后马上就书写关闭,便于排查代码中的问题,提高了代码的可读性
  2. 如果函数内有多个返回点,不需要多次书写cleanup代码,只要一次即可

不过在使用的时候要注意一点: 一定要在确保成功操作之后(如上述代码,确保文件成功打开之后)再执行defer cleanup,不然程序在打开f失败的时候,调用Close就会产生panic

 

Data

make and new

make创建的是对应type的一个实例 new创建的是一个type实例的指针

new和make都会将对象初始化为0

 

println , printf %s/%v recursion hell

当使用println/printf (%v/%s)打印自己定义的结构的信息时 要注意避免无线递归

看下面的代码

type Forever string

func main() {
        fmt.Printf("%+v", Forever("Hello forever"))
}

func (f Forever) String() (s string) {
        return fmt.Sprintf("%+v", f)
}

 

这段代码会导致无限递归死循环

原理就是 printf %s, %v会调用相应type的String()函数, 而在String函数内我们又使用了%v,又会去调用String()函数,因而就无限递归了

这里只要做一个修改就能避免这个递归问题

 

type Forever string

func main() {
        fmt.Printf("%+v", Forever("Hello forever"))
}

func (f Forever) String() (s string) {
        return fmt.Sprintf("%+v", string(f))
}

将 f 类型转换为string 内置的类型会自动进行处理,不会再去调用Forever类型的String()函数

 

Append a slice to a slice

将一个slice append到另一个slice的语法是这样的

sc := []int{1,2,3}
ss := []int{4,5,6}
sc = append(sc, ss...)

一定要有那三个”.”

 

Concurrency Tips

  • Only one goroutine can access the value at any time
  • Do not spawn unlimited go routines
  • using goroutines and channel can build non-blocking RPC system
  • [OFFTOPIC] Channel can use as queue, only in single goroutine(e.g leaky buffer)

One thought on “Effective Go & GoFAQ 阅读笔记

Leave a Reply

Your email address will not be published. Required fields are marked *

seven − 3 =

This site uses Akismet to reduce spam. Learn how your comment data is processed.