云计算领域的开源项目中,绝大多数使用 golang 作为开发语言。本文记录 golang 一些实用的语言特性。

函数 function

golang 不允许函数重载,函数重载在运行时进行类型匹配,这会造成性能损失。

golang 函数可以赋值给变量,变量获取函数的引用,并且知道函数的 signature, 如果两个变量保存同一个函数的引用,那么两个变量相等。

和许多其他语言一样,golang 也分传值(pass by value)和传引用(pass by reference),类型 slices、maps、interfaces、channels 默认为传引用。

使用命名返回值(named return)可以是代码更简洁:

func getX2AndX3_2(input int) (x2 int, x3 int) {
	x2 = 2 * input
	x3 = 3 * input
	// return x2, x3
	return
}

defer

将函数推迟到 return 或函数出差之后、 } 之前执行,类似 Java 或 C++ 的 finally,常用于释放资源。

带有参数的 defer 函数在执行执行的时候,参数值为 defer 当前行的变量值。多个 defer 执行的顺序满足 LIFO。

Defer is only executed at the return of a function, not at the end of a loop or some other limited scope.

func a() {
	i := 0
	defer fmt.Println(i)    // 打印0
	i++
	return
}

func f() {
	for i := 0; i < 5; i++ {
		defer fmt.Printf("%d ", i)  // 打印4 3 2 1 0
	}
}

defer 常见用法:

关闭文件流

// open a file
defer file.Close()

Unlock

mu.Lock()
defer mu.Unlock()

打印 footer

printHeader()
defer printFooter()

关闭数据库连接

// open a database connection
defer disconnectFromDB()

switch

switch key: {
case val1: fallthrough 
case val2, val3:
    // do something
default:
    // do something
}

switch {
case condition1:
    ...
case condition2:
    ...
default:
    ...
}

switch initialization {
case val1:
    ...
case val2:
    ...
default:
    ...
}

上面的 key 可以是任意类型,但 val1, val2, val3 必须是同一类型。与 Java/C/C++ 不一样的是,golang switch 的 break 是隐式的,如果想要穿透效果,需要使用 fallthrough

switch 后面如果没有变量,第一个为真的 case 将被执行。

switch 后面还可以包含初始化语句。

++ and --

golang 中的 ++ 和 – 只有后缀操作符,并且只能在声明的时候用,不能用作表达式。

n = i++         // validate
f(i++)          // 在golang中不允许
a[i] = b[i++]   // 在golang中不允许

这样的设计真是比 C/C++/Java 清爽了很多啊!

Init function

有一个特殊的函数叫做init(), 这个函数不能被调用,会在 main 函数执行之前自动调用,另外,当含有 init() 函数的 package 被 import 时,其 init() 函数也会被自动调用。

more to read: When is the init() function in Go (Golang) run?

Elegant Contants

type Stereotype int

const (
    TypicalNoob Stereotype = iota // 0
    TypicalHipster                // 1
    TypicalUnixWizard             // 2
    TypicalStartupFounder         // 3
)

type AudioOutput int

const (
    OutMute AudioOutput = iota // 0
    OutMono                    // 1
    OutStereo                  // 2
    _                          // skip value
    _
    OutSurround                // 5
)

type Allergen int

const (
    IgEggs Allergen = 1 << iota // 1 << 0 which is 00000001
    IgChocolate                         // 1 << 1 which is 00000010
    IgNuts                              // 1 << 2 which is 00000100
    IgStrawberries                      // 1 << 3 which is 00001000
    IgShellfish                         // 1 << 4 which is 00010000
)

type ByteSize float64

const (
    _           = iota             // ignore first value by assigning to blank identifier
    KB ByteSize = 1 << (10 * iota) // 1 << (10*1)
    MB                             // 1 << (10*2)
    GB                             // 1 << (10*3)
    TB                             // 1 << (10*4)
    PB                             // 1 << (10*5)
    EB                             // 1 << (10*6)
    ZB                             // 1 << (10*7)
    YB                             // 1 << (10*8)
)

const (
    Apple, Banana = iota + 1, iota + 2  // Apple:      1, Banana: 2
    Cherimoya, Durian                   // Cherimoya:  2, Durian: 3
    Elderberry, Fig                     // Elderberry: 3, Fig:    4
)