Skip to content

Latest commit

 

History

History
108 lines (81 loc) · 5.57 KB

09.Initialization.md

File metadata and controls

108 lines (81 loc) · 5.57 KB

Initialization

Although it doesn't look superficially very different from initialization in C or C++, initialization in Go is more powerful. Complex structures can be built during initialization and the ordering issues among initialized objects, even among different packages, are handled correctly.

在初始化方面,虽然表面上看起来与C或C++中没有太大区别,但 Go 中的初始化更强大。可以在初始化期间构建复杂的数据结构,并能正确处理对象之间的初始化顺序,即使在不同的包之间也能游刃有余。

Constants

Constants in Go are just that—constant. They are created at compile time, even when defined as locals in functions, and can only be numbers, characters (runes), strings or booleans. Because of the compile-time restriction, the expressions that define them must be constant expressions, evaluatable by the compiler. For instance, 1<<3 is a constant expression, while math.Sin(math.Pi/4) is not because the function call to math.Sin needs to happen at run time.

Go 的常量就仅仅是常量。它们在编译期被创建,即使是函数中的局部常量也是如此,而且只能是 numbers,characters (runes),strings 或 booleans 这些类型。受编译期的制约,定义它的表达式只能是常量表达式,由编译器求值。例如,1<<3 是一个常量表达式,而 math.Sin(math.Pi/4) 不是,因为 math.Sin 的函数调用发生在运行期间。

In Go, enumerated constants are created using the iota enumerator. Since iota can be part of an expression and expressions can be implicitly repeated, it is easy to build intricate sets of values.

Go 中的枚举常量由 iota 创建,因 iota 可参与表达式构建,且表达式可以隐式重复,所以可以用来轻松构建繁复的集合。

type ByteSize float64

const (
    _           = iota // ignore first value by assigning to blank identifier
    KB ByteSize = 1 << (10 * iota) // 1 左移 10 位 是 2 的 10 次方,下面依次类推
    MB
    GB
    TB
    PB
    EB
    ZB
    YB
)

The ability to attach a method such as String to any user-defined type makes it possible for arbitrary values to format themselves automatically for printing. Although you'll see it most often applied to structs, this technique is also useful for scalar types such as floating-point types like ByteSize.

为自定义类型实现 String 接口,使得任意类型都能够按需格式化。尽管这通常应用于结构体之上,但此技巧对标量类型同样适用,比如浮点类型 ByteSize 。

func (b ByteSize) String() string {
    switch {
    case b >= YB:
        return fmt.Sprintf("%.2fYB", b/YB)
    case b >= ZB:
        return fmt.Sprintf("%.2fZB", b/ZB)
    case b >= EB:
        return fmt.Sprintf("%.2fEB", b/EB)
    case b >= PB:
        return fmt.Sprintf("%.2fPB", b/PB)
    case b >= TB:
        return fmt.Sprintf("%.2fTB", b/TB)
    case b >= GB:
        return fmt.Sprintf("%.2fGB", b/GB)
    case b >= MB:
        return fmt.Sprintf("%.2fMB", b/MB)
    case b >= KB:
        return fmt.Sprintf("%.2fKB", b/KB)
    }
    return fmt.Sprintf("%.2fB", b)
}

The expression YB prints as 1.00YB, while ByteSize(1e13) prints as 9.09TB.

表达式 YB 打印 1.00YB ByteSize(1e13) 则打印 9.09TB

The use here of Sprintf to implement ByteSize's String method is safe (avoids recurring indefinitely) not because of a conversion but because it calls Sprintf with %f, which is not a string format: Sprintf will only call the String method when it wants a string, and %f wants a floating-point value.

这里的 Sprintf 用法是安全的(避免了无限循环),不是因为做了转换而是因为使用了 %f,它不是一个 String 格式: Sprintf 只有在期望得到一个字符串时才会调用 String,而 %f 希望得到一个浮点值。

Variables

Variables can be initialized just like constants but the initializer can be a general expression computed at run time.

变量可以像常量一样被初始化,不同的是可以使用普通的、运行时计算的表达式。

var (
    home   = os.Getenv("HOME")
    user   = os.Getenv("USER")
    gopath = os.Getenv("GOPATH")
)

The init function

Finally, each source file can define its own niladic init function to set up whatever state is required. (Actually each file can have multiple init functions.) And finally means finally: init is called after all the variable declarations in the package have evaluated their initializers, and those are evaluated only after all the imported packages have been initialized.

最后,每个源文件可定义各自的无参数的 init 函数,init 函数可用于做一些初始化工作。(事实上,每个文件可拥有多个 init 函数)init 在包中声明的变量全部被求值之后才会被调用,而且所有的 init 只有在所有被导入的包全部初始化之后才被调用。

Besides initializations that cannot be expressed as declarations, a common use of init functions is to verify or repair correctness of the program state before real execution begins.

除了弥补那些不能使用声明初始化的情形之外,init 函数的常见用途是在实际执行开始之前验证或修复程序状态的正确性。

func init() {
    if user == "" {
        log.Fatal("$USER not set")
    }
    if home == "" {
        home = "/home/" + user
    }
    if gopath == "" {
        gopath = home + "/go"
    }
    // gopath may be overridden by --gopath flag on command line.
    flag.StringVar(&gopath, "gopath", gopath, "override default GOPATH")
}