《Go语言精进之路》读书笔记 | 使用一致的变量声明形式

Posted COCOgsta

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《Go语言精进之路》读书笔记 | 使用一致的变量声明形式相关的知识,希望对你有一定的参考价值。

书籍来源:《Go语言精进之路:从新手到高手的编程思想、方法和技巧》

一边学习一边整理读书笔记,并与大家分享,侵权即删,谢谢支持!

附上汇总贴:《Go语言精进之路》读书笔记 | 汇总_COCOgsta的博客-CSDN博客


Go语言沿袭了静态编译型语言的传统:使用变量之前需要先进行变量的声明。

Go语言有两类变量。

包级变量(package variable):在package级别可见的变量。局部变量(local variable):函数或方法体内声明的变量,仅在函数或方法体内可见

8.1 包级变量的声明形式

包级变量只能使用带有var关键字的变量声明形式。从声明变量时是否延迟初始化对包级变量进行一次分类。

  1. 声明并同时显式初始化

在声明变量的同时进行显式初始化,实践中多使用下面的格式:

var variableName = InitExpression
复制代码

Go编译器会自动根据等号右侧的InitExpression表达式求值的类型确定左侧所声明变量的类型。

如果InitExpression采用的是不带有类型信息的常量表达式,比如下面的语句:

var a = 17
var f = 3.14
复制代码

则包级变量会被设置为常量表达式的默认类型:以整型值初始化的变量a,Go编译器会将之设置为默认类型int;而以浮点值初始化的变量f,Go编译器会将之设置为默认类型float64。

显式为包级变量a和f指定类型,那么有以下两种声明方式:

// 第一种
var a int32 = 17
var f float32 = 3.14

// 第二种
var a = int32(17)
var f = float32(3.14)
复制代码

Go语言官方更推荐后者。

  1. 声明但延迟初始化

对于声明时并不显式初始化的包级变量,使用最基本的声明形式:

var a int32
var f float64
复制代码

虽然没有显式初始化,但Go语言会让这些变量拥有初始的“零值”。

  1. 声明聚类与就近原则

Go语言提供var块用于将多个变量声明语句放在一起,一般将同一类的变量声明放在一个var块中,将不同类的声明放在不同的var块中;或者将延迟初始化的变量声明放在一个var块,而将声明并显式初始化的变量放在另一个var块中。笔者称之为“声明聚类”。比如:

// $GOROOT/src/net/http/server.go
var (
    bufioReaderPool   sync.Pool
    bufioWriter2kPool sync.Pool
    bufioWriter4kPool sync.Pool
)

var copyBufPool = sync.Pool 
    New: func() interface 
        b := make([]byte, 32*1024)
        return &b
    ,

...

// $GOROOT/src/net/net.go
var (
    aLongTimeAgo = time.Unix(1, 0)
    noDeadline = time.Time
    noCancel   = (chan struct)(nil)
)

var threadLimit chan struct
...
复制代码

copyBufPool变量没有被放入var块中,因为它的声明带有显式初始化,而var块中的变量声明都是延迟初始化的;hreadLimit被单独放在var块外面,考虑它是延迟初始化的变量声明,与var块中标识时间限制的变量有所不同。

大家可能有一个问题:是否应当将包级变量的声明全部集中放在源文件头部呢?使用静态编程语言的开发人员都知道,变量声明最佳实践中还有一条:就近原则,即尽可能在靠近第一次使用变量的位置声明该变量。就近原则实际上是变量的作用域最小化的一种实现手段。在Go标准库中我们很容易找到符合就近原则的变量声明例子,比如下面这个:

// $GOROOT/src/net/http/request.go
var ErrNoCookie = errors.New("http: named cookie not present")

func (r *Request) Cookie(name string) (*Cookie, error) 
    for _, c := range readCookies(r.Header, name) 
        return c, nil
    
    return nil, ErrNoCookie

复制代码

如果在源文件中,仅某个方法用到了包级变量,则可在临近该方法定义的位置声明该变量。如果一个包级变量在包内部被多处使用,那么这个变量还是放在源文件头部声明比较适合。

8.2 局部变量的声明形式

与包级变量相比,局部变量多了一种短变量声明形式,这也是局部变量采用最多的一种声明形式。

  1. 对于延迟初始化的局部变量声明,采用带有var关键字的声明形式

一种常见的采用带var关键字声明形式的变量是buf,另一种常见的采用带var关键字声明形式的变量是error类型的变量err。

  1. 对于声明且显式初始化的局部变量,建议使用短变量声明形式

短变量声明形式是局部变量最常用的声明形式:

a := 17
f := 3.14
s := "hello, gopher!"
复制代码

对于不接受默认类型的变量,依然可以使用短变量声明形式,只是在“:=”右侧要进行显式转型:

a := int32(17)
f := float32(3.14)
s := []byte("hello, gopher!")
复制代码
  1. 尽量在分支控制时应用短变量声明形式

很少单独声明在分支控制语句中使用的变量,而是通过短变量声明形式将其与if、for等融合在一起:

// $GOROOT/src/net/net.go
func (v *Buffers) WriteTo(w io.Writer) (n int64, err error) 
    // 笔者注:在if循环控制语句中使用短变量声明形式
    if wv, ok := w.(buffersWriter); ok 
        return wv.writeBuffers(v)
    
    // 笔者注:在for条件控制语句中使用短变量声明形式
    for _, b := range *v 
        nb, err := w.Write(b)
        n += int64(nb)
        if err != nil 
            v.consume(n)
            return n, err
        
    
    v.consume(n)
    return n, nil

复制代码

以上是关于《Go语言精进之路》读书笔记 | 使用一致的变量声明形式的主要内容,如果未能解决你的问题,请参考以下文章

《Go语言精进之路》读书笔记 | 了解string实现原理并高效使用

《Go语言精进之路》读书笔记 | 使用复合字面值作为初值构造器

《Go语言精进之路》读书笔记 | 理解Go语言表达式的求值顺序

《Go语言精进之路》读书笔记 | 汇总

《Go语言精进之路》读书笔记 | 使用无类型常量简化代码

《Go语言精进之路》读书笔记 | 使用iota实现枚举常量