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
    	}
    }
posted @ 2018-08-22 17:30  Przz  阅读(267)  评论(0编辑  收藏  举报