Golang的一些学习

Posted przz

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Golang的一些学习相关的知识,希望对你有一定的参考价值。

在 Go 中使用命名返回变量捕获 panic

在下面代码中,如果pressButton发生panic,那么不会执行到return err,导致返回的err是nil。

    func doStuff() error {  
        var err error
        // If there is a panic we need to recover in a deferred func
        defer func() {
            if r := recover(); r != nil {
                err = errors.New("the meeseeks went crazy!")
            }
        }()
    
        pressButton()
        return err
    }

可以使用命名返回变量解决,即使我们从未触碰到 doStuff 函数的末尾的返回语句,也会立刻返回这个 err 变量。

    func doStuff() (err error) {  
        // If there is a panic we need to recover in a deferred func
        defer func() {
            if r := recover(); r != nil {
                err = errors.New("the meeseeks went crazy!")
            }
        }()
    
        pressButton()
        return err
    }

重新切片(slice)

从 slice 中重新切出新 slice 时,新 slice 会引用原 slice 的底层数组,将导致难以预料的内存使用。可以通过拷贝临时 slice 的数据,而不是重新切片来解决:

    func get() (res []byte) {
        raw := make([]byte, 10000)
        fmt.Println(len(raw), cap(raw), &raw[0])    // 10000 10000 0xc420080000
        res = make([]byte, 3)
        copy(res, raw[:3])
        return
    }
    func test() {
        slice := a[2:3:4]
        //可以利用第三个参数控制容量
        //如果设置容量和长度相同,可以append时新创建底层数组,不影响原有的
    }

类型声明与方法

从一个现有的非 interface 类型创建新类型时,并不会继承原有的方法:

    // 定义 Mutex 的自定义类型
    type myMutex sync.Mutex
    
    func main() {
        var mtx myMutex
        mtx.Lock()
        mtx.UnLock()
    }
    //mtx.Lock undefined (type myMutex has no field or method Lock)…

如果你需要使用原类型的方法,可将原类型以匿名字段的形式嵌到你定义的新 struct 中:

    // 类型以字段形式直接嵌入
    type myLocker struct {
        sync.Mutex
    }
    
    func main() {
        var locker myLocker
        locker.Lock()
        locker.Unlock()
    }

for + 闭包函数

    type field struct {
        name string
    }
    
    func (p *field) print() {
        fmt.Println(p.name)
    }
    
    // 错误示例
    func main() {
        data := []field{{"one"}, {"two"}, {"three"}}
        for _, v := range data {
            go v.print()
        }
        time.Sleep(3 * time.Second)
        // 输出 three three three 
    }
    
    
    // 正确示例
    func main() {
        data := []field{{"one"}, {"two"}, {"three"}}
        for _, v := range data {
            v := v
            go v.print()
        }
        time.Sleep(3 * time.Second)
        // 输出 one two three
    }
    
    // 正确示例
    func main() {
        data := []*field{{"one"}, {"two"}, {"three"}}
        for _, v := range data {    // 此时迭代值 v 是三个元素值的地址,每次 v 指向的值不同
            go v.print()
        }
        time.Sleep(3 * time.Second)
        // 输出 one two three
    }

defer函数参数

对 defer 延迟执行的函数,它的参数会在声明时候就会求出具体值,而不是在执行时才求值:

    // 在 defer 函数中参数会提前求值
    func main() {
        var i = 1
        defer fmt.Println("result: ", func() int { return i * 2 }())
        i++
    }

select default

如果有default子句,case不满足条件时执行该语句。

如果没有default字句,select将阻塞,直到某个case可以运行;Go不会重新对channel或值进行求值。

缓冲通道中,刚好相反,由于元素值的传递是异步的,所以发送操作在成功向通道发送元素值之后就会立即结束(它不会关心是否有接收操作)。

如果使用非缓冲,ret值必须被接收。如果此函数等待超时直接返回nil,会导致Ret这个Chan在模块那边写不进去堵死。

    func (m *friendModule) Gift*****(cmd GiftCmd) *GiftRet {
        ctx, cancel := context.WithTimeout(context.Background(), util.ASyncCmdTimeOut)
        defer cancel()
        cmd.Ret = make(chan GiftRet, 1) ##非缓冲bug
        select {
        case m.GiftChan <- cmd:
        case <-ctx.Done():
            logs.Error("friend moduel gift cmdChan is full")
        }
        select {
        case ret := <-cmd.Ret:
            return &ret
        case <-ctx.Done():
            logs.Error("friend moduel gift cmdChan apply <-retChan timeout")
            return nil
        }
    }

以上是关于Golang的一些学习的主要内容,如果未能解决你的问题,请参考以下文章

代码片段 - Golang 实现简单的 Web 服务器

代码片段 - Golang 实现集合操作

Golang实践录:反射reflect的一些研究及代码汇总

IOS开发-OC学习-常用功能代码片段整理

Golang的一些学习

json [Golang] golang #golang #snippets中有用的片段