Go Little Book - 第五章 - 特点

第五章 - 特点(珍品?)(Chapter 5 - Tidbits)

In this chapter, we'll talk about a miscellany of Go's feature which didn't quite fit anywhere else.

在这一章是,我们会来介绍一些Go的特性的杂烩,这些内容不太适合放在其他章节中。

错误处理(Error Handling)

Go's preferred way to deal with errors is through return values, not exceptions. Consider the strconv.Atoi function which takes a string and tries to convert it to an integer:

Go更喜欢用返回值而不是异常的方式来处理错误。例如strconv.Atoi函数将一个字符串转换成一个整数:

package main

import (
  "fmt"
  "os"
  "strconv"
)

func main() {
  if len(os.Args) != 2 {
    os.Exit(1)
  }

  n, err := strconv.Atoi(os.Args[1])
  if err != nil {
    fmt.Println("not a valid number")
  } else {
    fmt.Println(n)
  }
}

You can create your own error type; the only requirement is that it fulfills the contract of the built-in error interface, which is:

你可以创建你自己的错误类型;唯一的要求就是需要实现内置接口error:

type error interface {
  Error() string
}

More commonly, we can create our own errors by importing the errors package and using it in the New function:

更为常见的是,我们可以通过导入errors包,并通过它的New函数来创建自己的错误:

import (
  "errors"
)


func process(count int) error {
  if count < 1 {
    return errors.New("Invalid count")
  }
  ...
  return nil
}

There's a common pattern in Go's standard library of using error variables. For example, the io package has an EOF variable which is defined as:

在GO的标准库中,使用错误变量是一个常用的模式。例如, 在io包中有一个EOF变量是这样定义的:

var EOF = errors.New("EOF")

This is a package variable (it's defined outside of a function) which is publicly accessible (upper-case first letter). Various functions can return this error, say when we're reading from a file or STDIN. If it makes contextual sense, you should use this error, too. As consumers, we can use this singleton:

这是一个公共(大写字母开关)的包变量(它定义有函数之外)。如果我们从文件或者标准输入时失败时,我们可以返回这个错误。为了更容易理解,你也应该用这个错误。作为使用者,我们可以用这个单件:

package main

import (
  "fmt"
  "io"
)

func main() {
  var input int
  _, err := fmt.Scan(&input)
  if err == io.EOF {
    fmt.Println("no more input!")
  }
}

As a final note, Go does have panic and recover functions. panic is like throwing an exception while recover is like catch; they are rarely used.

最后要注意的是,Go有panicrecover函数。panic像是抛出异常,而recover是捕获异常;它们不常使用。

Defer(Defer)

Even though Go has a garbage collector, some resources require that we explicitly release them. For example, we need to Close() files after we're done with them. This sort of code is always dangerous. For one thing, as we're writing a function, it's easy to forget to Close something that we declared 10 lines up. For another, a function might have multiple return points. Go's solution is the defer keyword:

虽然Go有垃圾回收机制,但是有些资源需要显示的释放它们。比如,当我们使用文件完了之后,需要调用Close()来关闭它们。这类代码总是很危险。其一,我们写一下函数的时候,如果申请一个资源超过10行,就很容易忘记Close。其二,一个函数可能会有多个返回点。Go的解决方案是使用defer关键字:

package main

import (
  "fmt"
  "os"
)

func main() {
  file, err := os.Open("a_file_to_read")
  if err != nil {
    fmt.Println(err)
    return
  }
  defer file.Close()
  // read the file
}

If you try to run the above code, you'll probably get an error (the file doesn't exist). The point is to show how defer works. Whatever you defer will be executed after the method returns, even if it does so violently. This lets you release resources near where it’s initialized and takes care of multiple return points.

如果你尝试运行上面的代码,你可能收到一个错误(文件不存在)。这里展示的是defer是如何工作的。无论如何在函数返回时defer都会被执行,虽然这样有点极端。但这可以让你在初始化附近释放资源和不要操心多个返回点的问题。

go fmt(go fmt)

Most programs written in Go follow the same formatting rules, namely, a tab is used to indent and braces go on the same line as their statement.

绝大多数Go写的代码遵守有一个相同的格式化规则,也就是说,使用Tab来缩进和花括号与语句同一行。

I know, you have your own style and you want to stick to it. That's what I did for a long time, but I'm glad I eventually gave in. A big reason for this is the go fmt command. It's easy to use and authoritative (so no one argues over meaningless preferences).

我知道你有自己的代码风格并且严格遵守它。我一直以来也是这么做的,但是我最终还是放弃了。一个最在的原因就是go fmt命令。它很容用且权威(所以没有人会为了毫无意义的偏好而争论)

When you're inside a project, you can apply the formatting rule to it and all sub-projects via:

当你在一个工程目录下,你可以通过下面的命令将工程下所有文件使用相同的格式化规则:

go fmt ./...

Give it a try. It does more than indent your code; it also aligns field declarations and alphabetically orders imports.

尝试一下吧。除了缩进代码,它还会自动对齐你的声明语句并将包导入按字母顺序排序。

If初始化(Initialized If)

Go supports a slightly modified if-statement, one where a value can be initiated prior to the condition being evaluated: Go提供了一种稍有不同的if声明,一个可以在条件执行之前声明和初始化:

if x := 10; count > x {
  ...
}

That's a pretty silly example. More realistically, you might do something like:

这是一个非常简单的例子。更实际的例子是,你可能是这样做的:

if err := process(); err != nil {
  return err
}

Interestingly, while the values aren't available outside the if-statement, they are available inside any else if or else.

有趣的是,if语句中定义并初始化的值在if语句之外是不可用的,仅可以在else ifelse语句中使用。

空接口和转换(Empty Interface and Conversions)

In most object-oriented languages, a built-in base class, often named object, is the superclass for all other classes. Go, having no inheritance, doesn't have such a superclass. What it does have is an empty interface with no methods: interface{}. Since every type implements all 0 of the empty interface's methods, and since interfaces are implicitly implemented, every type fulfills the contract of the empty interface.

在大多数面向对象语言中,都有一种内置的基类,叫object,它是所有其他类的超类。但是go语言不支持继承,所以没有类似的超类。Go确实有一个没有任何方法的空接口:interface{}。因为接口都是隐式实现,每种类型都实现了空接口的0个方法,所以每种类型都实现了空接口的协议。

If we wanted to, we could write an add function with the following signature:

如果我们愿意,我们可以通过下面声明方式写一个add函数:

func add(a interface{}, b interface{}) interface{} {
  ...
}

To convert an interface variable to an explicit type, you use .(TYPE):

将一个空接口变量转换成一个指定的类型,你可以使用.(TYPE):

return a.(int) + b.(int)

Note that if the underlying type is not int, the above will result in an error.

需要注意如果底层类型示是一个int,上面的代码会导致一个错误。

You also have access to a powerful type switch:

你也可以通过switch使用强大的类型转换:

switch a.(type) {
  case int:
    fmt.Printf("a is now an int and equals %d\n", a)
  case bool, string:
    // ...
  default:
    // ...
}

You'll see and probably use the empty interface more than you might first expect. Admittedly, it won't result in clean code. Converting values back and forth is ugly and dangerous but sometimes, in a static language, it's the only choice.

你会发现,空接口的使用会超出你的预期。诚然,这不会让代码变得简洁。来回转换值是丑陋和危险的,但有时候在静态类型语言中,这是唯一的选择。

字符串和字节数组(Strings and Byte Arrays)

Strings and byte arrays are closely related. We can easily convert one to the other:

字符串和字节数组有密切关系,我们可以轻易的将它们转换成对方:

stra := "the spice must flow"
byts := []byte(stra)
strb := string(byts)

In fact, this way of converting is common across various types as well. Some functions explicitly expect an int32 or an int64 or their unsigned counterparts. You might find yourself having to do things like:

事实上,这也是大多数类型的转换方式。一些函数明确指定一个int32或者int64或者相应的无符号类型。你可能会发现你自己不得不像下面这样做:

int64(count)

Still, when it comes to bytes and strings, it's probably something you'll end up doing often. Do note that when you use []byte(X) or string(X), you're creating a copy of the data. This is necessary because strings are immutable.

然而,当涉及到字节和字符串时,这可能是你会经常做的事。当你使用[]byte(X)或者string(X)时务必注意,你创建了数据的拷贝。这是因为字符串的不可变性。

Strings are made of runes which are unicode code points. If you take the length of a string, you might not get what you expect. The following prints 3:

当字符串有由unicode字符码runes组成时。如果你计算字符串的长度时,可能得到的结果和你期待的不同。下面结果是输出3:

fmt.Println(len("椒"))

If you iterate over a string using range, you'll get runes, not bytes. Of course, when you turn a string into a []byte you'll get the correct data.

如果你使用range迭代一个字符串,你得到的是runes,而不是bytes。当然,你将一个字符串转换为[]byte时,你得到的数据还是正确的。

函数类型(Function Type)

Functions are first-class types: 函数是第一类类型:

type Add func(a int, b int) int

which can then be used anywhere -- as a field type, as a parameter, as a return value.

可以在任何地方使用————可以做为一个字段,参数,返回值。

package main

import (
  "fmt"
)

type Add func(a int, b int) int

func main() {
  fmt.Println(process(func(a int, b int) int{
      return a + b
  }))
}

func process(adder Add) int {
  return adder(1, 2)
}

Using functions like this can help decouple code from specific implementations much like we achieve with interfaces.

像这样使用函数可以使你在一些特定实现时减少代码的耦合性,就像使用接口实现那样。

继续之前(Before You Continue)

We looked at various aspects of programming with Go. Most notably, we saw how error handling behaves and how to release resources such as connections and open files. Many people dislike Go's approach to error handling. It can feel like a step backwards. Sometimes, I agree. Yet, I also find that it results in code that's easier to follow. defer is an unusual but practical approach to resource management. In fact, it isn't tied to resource management only. You can use defer for any purpose, such as logging when a function exits.

我们已经学习了Go编程的很多内容。显而易见,我们看见了错误处理的行为和资源释放如连接或者打开文件。很多人不喜欢Go的错误处理方式。它让人觉得这是一种退步。有些时候,我同意这种说法。然而,我也发现这会导致代码更易读。defer是一种不常见但很实用的资源管理手段。事实上,它不仅仅可以进行资源管理。你可以使用defer完成任何目的,例如当一个函数退出时打印日志记录。

Certainly, we haven't looked at all of the tidbits Go has to offer. But you should be feeling comfortable enough to tackle whatever you come across.

当然,我们还没有学习Go提供的所有花絮。但是无论你遇到什么你应该可以轻松应对。

转载于:https://my.oschina.net/airhead/blog/2980486

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值