07.变量声明须一致

GNU精神
• 阅读 1776

本文视频地址

Go 语言,使用变量之前需要先进行变量的声明。

var s string = “Golang"
n := 666

Go 语言有两类变量

包级别(package varible):在 package 级别可见的变量。如果是导出变量,该变量也可以被视为全局变量;

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

1. 包级别变量

包级变量使用var 关键字的变量声明形式,从变量声明的时候是否延迟初始化这个角度对包级变量进行一次分类。

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

    /$GOROOT/src/io/pipe.go
    var ErrClosedPipe = errors.New("io: read/write on closed pipe")
         
    //$GOROOT/src/io/io.go
    var EOF = errors.New("EOF”)

上面,对于变量声明的同时进行显式初始化的这类包级变量,格式:

     var 变量名 = 初始化表达式

编译器会自动根据等号右侧 初始化表达式 结果值的类型确定左侧声明的变量的类型。

  var num = 16
  var pi = 3.14

Go 会为包级变量设置默认类型,对于未显式赋予类型的整型变量 num,编译器会设置默认 类型 int;而浮点型变量 f 的默认类型则为 float64。如果不想使用默认类型,而是要显式为 num 和 pi 指定类型。

 var num = int32(16)
 var pi  = float64(3.14)

上面是默认类型和显式指定类型两种声明形式,尤其是在将这些变量放在一个 var 块中声明时

    var (
      num = 17
      pi = float32(3.14)
   )

### 2) 延迟初始化

有时候,声明并不立即显式初始化的包级变量,虽然没有显式初始化,Go 语言也会让这些变量拥有初始的“零值”。如果是自定义的类型,保证其零值可用是非常必要的,形式如下:
  var a int32
  var f float64

3) 声明聚类与就近原则

Go 语言提供 var 块用于将多于一个的变量声明放在一起,我们习惯:
1 将同一类的变量声明放在一个 var 块中,不同类的声明放在不同的 var 块中;

        2 将延迟初始化的变量声明放在一个 var 块,而将声明且显式初始化的变量放在另一个 var 块中。
       

Go 标准库中的代码:

// $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
    },
}

copyBufPool 变量没有放入 var 块就是因为它的声明是带有显式初始化的,而 var 块中的变量声明都是延迟初始化的。

// $GOROOT/src/net/net.go
var (
    // aLongTimeAgo is a non-zero time, far in the past, used for
    // immediate cancelation of dials.
    aLongTimeAgo = time.Unix(1, 0)

    // nonDeadline and noCancel are just zero values for
    // readability with functions taking too many parameters.
    noDeadline = time.Time{}
    noCancel   = (chan struct{})(nil)
)

var threadLimit chan struct{}

hreadLimit 单独放在 var 块外面,它是延迟初始化的变量声明,它与以上的 var 块中时间限制的变量也有所不同。

变量声明最佳实践中还有一个原则-就近原则,如果一个包级变量在包内部多处使用,放在源文件头部声明比较恰当直观。

/ $GOROOT/src/net/http/request.go

// ErrNoCookie is returned by Request's Cookie method when a cookie is not found.
var ErrNoCookie = errors.New("http: named cookie not present")

// Cookie returns the named cookie provided in the request or
// ErrNoCookie if not found.
// If multiple cookies match the given name, only one cookie will
// be returned.
func (r *Request) Cookie(name string) (*Cookie, error) {
    for _, c := range readCookies(r.Header, name) {
        return c, nil
    }
    return nil, ErrNoCookie
}

2. 局部变量的声明形式

比上面的包级别变量,局部变量多了短变量声明形式,局部变量采用最多的一种声明形式。
##### 1) 对于延迟初始化的局部变量声明,用带有 var 的声明形式。

        // $GOROOT/src/strings/replace.go
         func (r *byteReplacer) Replace(s string) string {
             var buf []byte // 延迟初始化
             for i := 0; i < len(s); i++ {
                 b := s[i]
                 if r[b] != b {
                 if buf == nil {
                     buf = []byte(s)
                 }
                 buf[i] = r[b]
                 }
             }
             if buf == nil {
                 return s
             }
             return string(buf)
             }
 var err error  这也是我们日常常见的声明方式

短变量声明形式是局部变量最常用的声明形式,在 Go 标准库代码中广泛使用。

 num := 17                  //默认类型
 pi := float32(3.14)   //显示转型


// $GOROOT/src/net/net.go
func (v *Buffers) WriteTo(w io.Writer) (n int64, err error) {
    if wv, ok := w.(buffersWriter); ok { 
        return wv.writeBuffers(v)
    } 
 
    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源代码net中,if wv, ok := w.(buffersWriter); ok和for _, b := range *v都使用了短变量声明形式,这也体现出“就近”原则,让变量的作用域最小化。

设计良好的函数/方法的原则是“单一职责”,因此每个函数/方法规模都不大,很少需要应用 var 块来声明局部变量,如果你在声明局部变量时遇到这种场景,那就使用 var 块来声明多于一个的局部变量吧。看一下Go标准库中的源代码是这么使用的:

// $GOROOT/src/net/dial.go
func (r *Resolver) resolveAddrList(ctx context.Context, op, network, 
                            addr string, hint Addr) (addrList, error) {
    ... ...
    var (
        tcp      *TCPAddr
        udp      *UDPAddr
        ip       *IPAddr
        wildcard bool
    )
   ... ...
}

07.变量声明须一致

点赞
收藏
评论区
推荐文章
小嫌 小嫌
3年前
Javascript中的变量提升
定义JavaScript中奇怪的一点是你可以在变量和函数声明之前使用它们。就好像是变量声明和函数声明被提升了代码的顶部一样。sayHi()//Hithere!functionsayHi()console.log('Hithere!')name'JohnDoe'console.log(name)//JohnDoevarn
Wesley13 Wesley13
3年前
java成员变量的初始化
类变量(static变量,不需要实例化对象也可以引用)实例变量(非static变量,需要实例化对象)局部变量(类的成员函数中的变量)初始化方式:构造函数初始化变量声明时初始化代码块初始化java自动初始化(在构造函数执行之前执行) java保证所有变量被使用之前都是经过初始化的(声明并且定义过,被赋值
菜园前端 菜园前端
2年前
JavaScript的基础语法介绍
原文链接:变量声明在JavaScript程序中,使用一个变量之前应当先声明。变量是使用关键字var来声明的。javascriptvarivarsum20如果未在var声明语句中给变量赋予初始值,那么虽然声明了这个变量,但在给它传入一个值之前,它的初始值就是
Stella981 Stella981
3年前
Sass 变量的声明与使用
本节我们学习Sass中的变量,那么什么是变量呢?Sass中的变量可以用来存储一些信息,并且重复使用。变量可以存储的信息包括字符串、数字、布尔值、颜色值等等。声明变量Sass中声明变量需要用到$符号,格式为:$变量名:变量值;示例:例如下面我们定义一个变量,并给这个变量赋了一个颜色
Stella981 Stella981
3年前
Rust学习笔记#2:变量声明、绑定与引用
!(https://oscimg.oschina.net/oscnet/up2a26b061f3ae6214fc3a12b58a2ad0e8433.JPEG)变量声明基础知识Rust中的变量分局部变量和全局变量两种,且必须先声明后使用,常见的声明语法为://声明局部变量,使用let关键字letva
Wesley13 Wesley13
3年前
Go 变量声明
变量命名命名方法varnametype是定义单一变量的语法packagemainimport"fmt"funcmain(){varageint//variabledeclarationfmt.Println("Mya
Wesley13 Wesley13
3年前
go语言学习笔记(第2章)—类型部分
第2章:顺序编程GO语言被称为"更好的C语言"1\.变量1)变量的声明GO语言引入了关键字 var,而类型信息放在变量名之后例如:varv1int可以将多个变量声明放在一起,例如:var(        v1int        v2string)
Stella981 Stella981
3年前
JavaScript变量声明
const,let,var的区别和用法1.const——声明一个只读的常量,在声明的时候给其赋初值,之后不能再进行赋值。1consti0;2console.log(i);//有输出,为0如果对i进行再次赋值则会报错,TypeError:Assignmenttoconstantvariable
Stella981 Stella981
3年前
GO开发[二]:golang语言基础
一.变量1.声明变量变量相当于是对一块数据存储空间的命名,程序可以通过定义一个变量来申请一块数据存储空间,之后可以通过引用变量名来使用这块存储空间。Go语言引入了关键字var,而类型信息放在变量名之后,变量声明语句不需要使用分号作为结束符,示例如下:varv1intvarv2string
Wesley13 Wesley13
3年前
Java变量类型
在Java语言中,所有的变量在使用前必须声明。声明变量的基本格式如下:typeidentifiervalue,identifiervalue...;格式说明:type为Java数据类型。identifier是变量名。可以使用逗号隔开来声明多个同类型变量。Java语言支持的变量类型有:类变量:独
Easter79 Easter79
3年前
Systemverilog for design 笔记(四)
转载请标明出处数组、结构体和联合体1. 结构体(struct)1.1. 结构体声明结构体默认是变量,也可以声明为线网varstruct {//通过var进行结构体变量声明logic\31:0\a,b;