Effective Go : 方法(一)

105 阅读2分钟

这是我参与11月更文挑战的第20天,活动详情查看:2021最后一次更文挑战

返回多个值

Go 不寻常的特性之一是函数和方法可以返回多个值。 这种形式可用于改进 C 程序中的几个笨拙的习惯用法:例如 -1 表示 EOF ,通过地址修改参数。

在 Go 中,例如Write这个方法,可以返回一个数字和一个error:“是的,你写了一些字节,但不是全部,因为你填满了设备”。 来自包 os 的文件的 Write 方法在包os中的签名是:

func (file *File) Write(b []byte) (n int, err error)

正如文档所说,当n != len(b) 时,方法返回写入的字节数和非零错误。 这是一种常见的风格; 有关更多示例,请参阅错误处理部分。

类似的方法不需要传递指向返回值的指针来模拟引用参数。 下面是一个简单的函数,从字节切片中的某个位置抓取一个数字,返回该数字和下一个位置。

func nextInt(b []byte, i int) (int, int) {
    for ; i < len(b) && !isDigit(b[i]); i++ {
    }
    x := 0
    for ; i < len(b) && isDigit(b[i]); i++ {
        x = x*10 + int(b[i]) - '0'
    }
    return x, i
}

您可以使用它来扫描输入切片 b 中的数字,如下所示:

for i := 0; i < len(b); {
  x, i = nextInt(b, i)
  fmt.Println(x)
}

Defer

Go 的 defer 语句会在执行 defer 的函数返回之前,安排一个函数调用(延迟函数)来运行。 这是一种不寻常但有效的方法,例如处理诸如必须释放资源的情况,这种场景下不管函数采用哪条路径返回,都必须进行释放资源。 很好的示例是解锁互斥锁或关闭文件。

// Contents returns the file's contents as a string.
func Contents(filename string) (string, error) {
    f, err := os.Open(filename)
    if err != nil {
        return "", err
    }
    defer f.Close()  // f.Close will run when we're finished.
​
    var result []byte
    buf := make([]byte, 100)
    for {
        n, err := f.Read(buf[0:])
        result = append(result, buf[0:n]...) // append is discussed later.
        if err != nil {
            if err == io.EOF {
                break
            }
            return "", err  // f will be closed if we return here.
        }
    }
    return string(result), nil // f will be closed if we return here.
}

推迟对 Close 等函数的调用有两个优点。 首先,它保证您永远不会忘记关闭文件,经常的,我们编程的时候,中断了一会儿回来,会遗忘该函数需要最后关闭资源,很容易犯这个错误。 其次,语句放在开头比起放在末尾,会更加清晰。