二、golang文档

If

In Go a simple if looks like this:

if x > 0 {
    return y
}

Mandatory braces encourage writing simple if statements on multiple lines. It's good style to do so anyway, especially when the body contains a control statement such as a return or break.

Since if and switch accept an initialization statement, it's common to see one used to set up a local variable.

if err := file.Chmod(0664); err != nil {
    log.Print(err)
    return err
}

In the Go libraries, you'll find that when an if statement doesn't flow into the next statement—that is, the body ends in break, continue, goto, or return—the unnecessary else is omitted.

f, err := os.Open(name)
if err != nil {
    return err
}

codeUsing(f)
This is an example of a common situation where code must guard against a sequence of error conditions. The code reads well if the successful flow of control runs down the page, eliminating error cases as they arise. Since error cases tend to end in return statements, the resulting code needs no else statements.

f, err := os.Open(name)
if err != nil {
    return err
}
d, err := f.Stat()
if err != nil {
    f.Close()
    return err
}

codeUsing(f, d)

这段文字主要讨论了Go语言中if语句的使用和风格。

1、简单的if语句:

在Go中,一个简单的if语句看起来如下所示:

if x > 0 {
    return y
}

强制使用大括号鼓励开发者将简单的if语句分为多行来编写。尤其当if语句的主体包含如return或break这样的控制语句时,多行编写是一个好的风格。
2、初始化语句:

由于if和switch都接受一个初始化语句,所以常常看到其用于设置局部变量。

if err := file.Chmod(0664); err != nil {
    log.Print(err)
    return err
}

3、省略不必要的else:

在Go的库中,你会发现当一个if语句的主体并不流入到下一个语句时(即,主体以breakcontinuegotoreturn结束),那么不必要的 else 会被省略。

f, err := os.Open(name)
if err != nil {
    return err
}
codeUsing(f)

4、处理错误情况:

这是一个常见的情况,代码必须针对一系列的错误情况进行防护。如果成功的控制流顺序地从上往下运行,并随着错误情况的出现而消除它们,那么代码的可读性会更好。因为错误情况往往会以return语句结束,所以得到的代码不需要else语句。

f, err := os.Open(name)
if err != nil {
    return err
}
d, err := f.Stat()
if err != nil {
    f.Close()
    return err
}
codeUsing(f, d)

总之,这段话描述了在Go中使用if语句的惯用法和风格,强调了代码的清晰性和错误处理的重要性。

Redeclaration and reassignment
An aside: The last example in the previous section demonstrates a detail of how the := short declaration form works. The declaration that calls os.Open reads,

f, err := os.Open(name)
This statement declares two variables, f and err. A few lines later, the call to f.Stat reads,

d, err := f.Stat()
which looks as if it declares d and err. Notice, though, that err appears in both statements. This duplication is legal: err is declared by the first statement, but only re-assigned in the second. This means that the call to f.Stat uses the existing err variable declared above, and just gives it a new value.

In a := declaration a variable v may appear even if it has already been declared, provided:

this declaration is in the same scope as the existing declaration of v (if v is already declared in an outer scope, the declaration will create a new variable §),
the corresponding value in the initialization is assignable to v, and
there is at least one other variable that is created by the declaration.
This unusual property is pure pragmatism, making it easy to use a single err value, for example, in a long if-else chain. You'll see it used often.

§ It's worth noting here that in Go the scope of function parameters and return values is the same as the function body, even though they appear lexically outside the braces that enclose the body.

这段话讨论了Go语言中的变量重声明和重新赋值的概念和规则。我将为您分点解释:

1、重声明与重新赋值:
文中给出了一个关于os.Open和f.Stat的示例,其中err变量在两个语句中都出现了。这种做法在Go中是允许的。在第一个语句中,err被声明,而在第二个语句中,err只是被重新赋值,而不是重新声明。

f, err := os.Open(name)   // 声明 f 和 err
...
d, err := f.Stat()       // 声明 d, 但只是重新赋值 err

2、:=短声明的详细规则:
(1)在:=声明中,即使变量v已经被声明过,它仍然可以再次出现在新的:=声明中,但必须满足以下条件:

  • 这次声明和已有的v声明在相同的作用域内。如果v在外层作用域已经被声明过,那么这次声明会创建一个新的变量。
  • 初始化中与v对应的值可以赋给v。
  • 声明中至少有一个其它的变量是新创建的。

3、为何允许这种特性:
这种特性是基于实用性考虑的。特别是当在长的if-else链中使用一个单一的err值时,这种特性很有用,可以简化代码。您在实际的Go代码中会经常看到这种用法。

4、函数参数和返回值的作用域:
文章还指出,虽然函数参数和返回值从字面上看似乎在函数体的大括号外部,但它们的作用域实际上和函数体是相同的。
简而言之,这段话说明了Go中关于变量重声明和重新赋值的规则,以及为什么这些规则存在,它们如何简化代码,并提醒读者注意函数参数和返回值的作用域。

For
The Go for loop is similar to—but not the same as—C's. It unifies for and while and there is no do-while. There are three forms, only one of which has semicolons.

// Like a C for

for init; condition; post { }

// Like a C while

for condition { }

// Like a C for(;;)

for { }

hort declarations make it easy to declare the index variable right in the loop.

sum := 0
for i := 0; i < 10; i++ {
    sum += i
}

If you're looping over an array, slice, string, or map, or reading from a channel, a range clause can manage the loop.

for key, value := range oldMap {
    newMap[key] = value
}

If you only need the first item in the range (the key or index), drop the second:

for key := range m {
    if key.expired() {
        delete(m, key)
    }
}

If you only need the second item in the range (the value), use the blank identifier, an underscore, to discard the first:

sum := 0
for _, value := range array {
    sum += value
}

For strings, the range does more work for you, breaking out individual Unicode code points by parsing the UTF-8. Erroneous encodings consume one byte and produce the replacement rune U+FFFD. (The name (with associated builtin type) rune is Go terminology for a single Unicode code point. See the language specification for details.) The loop

for pos, char := range "日本\x80語" { // \x80 is an illegal UTF-8 encoding
    fmt.Printf("character %#U starts at byte position %d\n", char, pos)
}

prints

character U+65E5 '日' starts at byte position 0
character U+672C '本' starts at byte position 3
character U+FFFD '�' starts at byte position 6
character U+8A9E '語' starts at byte position 7

Finally, Go has no comma operator and ++ and -- are statements not expressions. Thus if you want to run multiple variables in a for you should use parallel assignment (although that precludes ++ and --).

// Reverse a

for i, j := 0, len(a)-1; i < j; i, j = i+1, j-1 {
    a[i], a[j] = a[j], a[i]
}

这段话主要解释了Go中for循环的使用和与C语言中for循环的一些区别。下面我将逐点为您解释。

1、Go的for循环基本形式:

(1)Go有三种for循环形式:

与C语言中的for很像,包含初始化、条件和后置语句。

  • for init; condition; post { }
    类似于C语言中的while,只有一个条件。

  • for condition { }
    无限循环,与C语言中的for(;;)类似。

  • for { }

2、短变量声明在循环中的应用:

在循环中,您可以使用短变量声明(:=)直接在循环中声明索引变量。

sum := 0
for i := 0; i < 10; i++ {
    sum += i
}

3、使用range遍历:

  • 当您需要遍历数组、切片、字符串、映射或从通道中读取时,可以使用range子句。
for key, value := range oldMap {
    newMap[key] = value
}
  • 如果只需要range的第一个项(即键或索引),则可以省略第二个。
for key := range m {
    if key.expired() {
        delete(m, key)
    }
}
  • 如果只需要range的第二个项(即值),则使用空白标识符_来丢弃第一个。
sum := 0
for _, value := range array {
    sum += value
}

4、range与字符串:

对于字符串,range会为您完成更多的工作,它会解析UTF-8字符并为您提取单个的Unicode码点。如果有错误的编码,它会使用替代字符U+FFFD。

例如,字符串"日本\x80語"中的\x80是一个非法的UTF-8编码,使用range遍历这个字符串时,该非法编码会被替换为U+FFFD。

5、++和--在Go中的特性:

Go中没有逗号运算符,且++和--在Go中是语句而不是表达式。因此,如果您想在for循环中同时运行多个变量,您应该使用并行赋值。但是这种方式不支持++和--。

for i, j := 0, len(a)-1; i < j; i, j = i+1, j-1 {
a[i], a[j] = a[j], a[i]
}
总的来说,这段话详细介绍了Go语言中for循环的各种用法和特性,并与C语言中的for循环进行了比较。

Switch

Go's switch is more general than C's. The expressions need not be constants or even integers, the cases are evaluated top to bottom until a match is found, and if the switch has no expression it switches on true. It's therefore possible—and idiomatic—to write an if-else-if-else chain as a switch.

func unhex(c byte) byte {
    switch {
    case '0' <= c && c <= '9':
        return c - '0'
    case 'a' <= c && c <= 'f':
        return c - 'a' + 10
    case 'A' <= c && c <= 'F':
        return c - 'A' + 10
    }
    return 0
}

There is no automatic fall through, but cases can be presented in comma-separated lists.

func shouldEscape(c byte) bool {
    switch c {
    case ' ', '?', '&', '=', '#', '+', '%':
        return true
    }
    return false
}

Although they are not nearly as common in Go as some other C-like languages, break statements can be used to terminate a switch early. Sometimes, though, it's necessary to break out of a surrounding loop, not the switch, and in Go that can be accomplished by putting a label on the loop and "breaking" to that label. This example shows both uses.

Loop:
    for n := 0; n < len(src); n += size {
        switch {
        case src[n] < sizeOne:
            if validateOnly {
                break
            }
            size = 1
            update(src[n])

        case src[n] < sizeTwo:
            if n+1 >= len(src) {
                err = errShortInput
                break Loop
            }
            if validateOnly {
                break
            }
            size = 2
            update(src[n] + src[n+1]<<shift)
        }
    }

Of course, the continue statement also accepts an optional label but it applies only to loops.

To close this section, here's a comparison routine for byte slices that uses two switch statements:

// Compare returns an integer comparing the two byte slices,
// lexicographically.
// The result will be 0 if a == b, -1 if a < b, and +1 if a > b

func Compare(a, b []byte) int {
    for i := 0; i < len(a) && i < len(b); i++ {
        switch {
        case a[i] > b[i]:
            return 1
        case a[i] < b[i]:
            return -1
        }
    }
    switch {
    case len(a) > len(b):
        return 1
    case len(a) < len(b):
        return -1
    }
    return 0
}

这段文本详细描述了 Go 语言中 switch 语句的一些特性和用法。让我们一步一步解释:

1、更为通用的 switch:

Go 的 switch 语句比 C 语言更为通用。其表达式不必是常量或整数。
case 从上到下逐个评估,直到找到匹配的项。
如果 switch 没有表达式,它会默认为 true。这使得我们可以将 if-else-if-else 链条写为一个 switch 语句。
例如,unhex 函数根据输入字节(代表十六进制的字符)返回其十进制值。

2、没有自动的 fallthrough:

func shouldEscape(c byte) bool {
    switch c {
    case ' ', '?', '&', '=', '#', '+', '%':
        return true
    }
    return false
}

在大多数的 C-like 语言中,switch 的一个 case 执行完后会自动执行下一个 case,除非使用 break 中断。但在 Go 中不是这样的。Go 执行一个case则结束不继续执行。
然而,可以使用逗号分隔的列表来表示多个值。例如,shouldEscape 函数检查一个字节是否应该被转义。

3、break 的使用:

Loop:
    for n := 0; n < len(src); n += size {
        switch {
        case src[n] < sizeOne:
            if validateOnly {
                break
            }
            size = 1
            update(src[n])

        case src[n] < sizeTwo:
            if n+1 >= len(src) {
                err = errShortInput
                break Loop
            }
            if validateOnly {
                break
            }
            size = 2
            update(src[n] + src[n+1]<<shift)
        }
    }

在 Go 中,可以使用 break 语句提前结束一个 switch。
有时,你可能需要跳出外部的循环而不是 switch。在 Go 中,这可以通过给循环添加一个标签,并 "break" 到那个标签来实现。
上面的例子中展示了两种用法:在一个 switch 内部提前跳出和在一个 switch 内部跳出外部循环。

4、带标签的 continue:

continue 语句也接受一个可选的标签,但它只适用于循环。

5、比较字节切片的例子:

func Compare(a, b []byte) int {
    for i := 0; i < len(a) && i < len(b); i++ {
        switch {
        case a[i] > b[i]:
            return 1
        case a[i] < b[i]:
            return -1
        }
    }
    switch {
    case len(a) > len(b):
        return 1
    case len(a) < len(b):
        return -1
    }
    return 0
}

最后,文中给出了一个 Compare 函数,它用于比较两个字节切片。
这个函数用两个 switch 语句进行字节切片的比较,并根据词典序返回 -1、0 或 1。
总之,文本展示了 Go 中 switch 语句的强大和灵活性,并通过实例说明了如何在实际情境中使用它。

Type switch

A switch can also be used to discover the dynamic type of an interface variable. Such a type switch uses the syntax of a type assertion with the keyword type inside the parentheses. If the switch declares a variable in the expression, the variable will have the corresponding type in each clause. It's also idiomatic to reuse the name in such cases, in effect declaring a new variable with the same name but a different type in each case.

var t interface{}
t = functionOfSomeType()
switch t := t.(type) {
default:
    fmt.Printf("unexpected type %T\n", t)     // %T prints whatever type t has
case bool:
    fmt.Printf("boolean %t\n", t)             // t has type bool
case int:
    fmt.Printf("integer %d\n", t)             // t has type int
case *bool:
    fmt.Printf("pointer to boolean %t\n", *t) // t has type *bool
case *int:
    fmt.Printf("pointer to integer %d\n", *t) // t has type *int
}

这段话描述了如何使用switch来确定接口变量的动态类型,这种操作被称为类型开关(type switch)。下面我为您逐点解释。

1、类型开关的基本概念:
当我们有一个接口类型的变量时,这个变量可以持有很多其他类型的值。但有时我们可能想知道它当前持有的值具体是什么类型。为了完成这一操作,Go提供了类型开关。

2、语法:
类型开关的语法与类型断言相似,但在括号内使用type关键字。

3、声明变量:
如果switch在其表达式中声明了变量,那么在每个case子句中,这个变量都将具有相应的类型。习惯上,我们在这种情况下重用变量名,实际上是在每个case中使用相同的名字声明一个不同类型的新变量。

示例代码解释:

var t interface{}
t = functionOfSomeType()
switch t := t.(type) {

var t interface{}: 声明一个名为t的空接口类型变量。空接口可以容纳任何类型的值。
t = functionOfSomeType(): 假设functionOfSomeType是某个返回各种类型值的函数,我们将其结果赋给t。
switch t := t.(type): 这是一个类型开关的开始。它检查t的动态类型。

default:
    fmt.Printf("unexpected type %T\n", t)     // %T prints whatever type t has
default子句处理t不匹配任何其他case子句中的类型的情况。
case bool:
    fmt.Printf("boolean %t\n", t)             // t has type bool

这个case子句处理当t是bool类型的情况。在这个子句内,变量t被认为是bool类型。
其他case子句与此类似,每个case都处理t可能的一个类型,并在该case内部,t被认为是那个特定的类型。

这种类型开关特别有用,因为它允许我们在同一个switch语句中处理不同的类型,并为每种类型提供专门的处理逻辑。

Functions

Multiple return values

One of Go's unusual features is that functions and methods can return multiple values. This form can be used to improve on a couple of clumsy idioms in C programs: in-band error returns such as -1 for EOF and modifying an argument passed by address.

In C, a write error is signaled by a negative count with the error code secreted away in a volatile location. In Go, Write can return a count and an error: “Yes, you wrote some bytes but not all of them because you filled the device”. The signature of the Write method on files from package os is:

func (file *File) Write(b []byte) (n int, err error)
and as the documentation says, it returns the number of bytes written and a non-nil error when n != len(b). This is a common style; see the section on error handling for more examples.

A similar approach obviates the need to pass a pointer to a return value to simulate a reference parameter. Here's a simple-minded function to grab a number from a position in a byte slice, returning the number and the next position.

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
}

You could use it to scan the numbers in an input slice b like this:

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

这部分描述了 Go 语言的函数中一项独特的特性:可以返回多个值。以下是对每个部分的详细解释。

1、多个返回值:
Go 允许函数和方法返回多个值,这与其他许多编程语言不同。这个特性可以优化某些 C 语言中笨拙的惯用法,例如使用 -1 表示 EOF 或通过地址修改传入的参数。

在 C 语言中,写入错误是通过负的计数来表示的,并将错误代码保存在易失的位置。而在 Go 中,写入操作可以返回写入的字节数以及一个错误。这意味着你可以得到“是的,你写入了一些字节,但并不是所有的字节,因为你填满了设备”。

2、例子:
func (file *File) Write(b []byte) (n int, err error)
文档中给出了 os 包中文件的 Write 方法的一个例子,该方法返回两个值:写入的字节数和一个错误。如果写入的字节数不等于输入切片的长度,那么将返回一个非零的错误。这是一个常见的编码风格。

3、引用参数模拟:

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
}
    for i := 0; i < len(b); {
        x, i = nextInt(b, i)
        fmt.Println(x)
    }

在 C 语言中,你可能需要通过指针传递一个返回值,以模拟一个引用参数。而在 Go 中,你可以直接返回该值。文档提供了一个函数 nextInt,它从字节切片的某个位置提取一个数字,并返回该数字及其后的位置。这样,你可以避免使用指针或修改原始切片。

4、如何使用:
文档的最后部分给出了如何使用 nextInt 函数来扫描输入切片 b 中的数字的示例。你可以在一个循环中调用 nextInt,并持续更新索引 i,直到你处理完整个切片。

总的来说,这部分强调了 Go 语言中函数可以返回多个值的优势,并通过实际的代码示例展示了如何使用这一特性。

Named result parameters

The return or result "parameters" of a Go function can be given names and used as regular variables, just like the incoming parameters. When named, they are initialized to the zero values for their types when the function begins; if the function executes a return statement with no arguments, the current values of the result parameters are used as the returned values.

The names are not mandatory but they can make code shorter and clearer: they're documentation. If we name the results of nextInt it becomes obvious which returned int is which.

func nextInt(b []byte, pos int) (value, nextPos int) {
Because named results are initialized and tied to an unadorned return, they can simplify as well as clarify. Here's a version of io.ReadFull that uses them well:

func ReadFull(r Reader, buf []byte) (n int, err error) {
    for len(buf) > 0 && err == nil {
        var nr int
        nr, err = r.Read(buf)
        n += nr
        buf = buf[nr:]
    }
    return
}

这段文字讲述了 Go 语言中函数的返回结果参数可以被命名的特性,并且这些命名的结果参数可以像普通的变量一样使用。以下是对这部分的详细解释:

1、命名的返回结果参数:
在 Go 中,函数的返回值可以被命名,就像函数的输入参数一样。这些被命名的返回值在函数开始执行时会被初始化为其类型的零值。如果函数执行了一个没有任何参数的 return 语句,那么这些命名的返回值参数的当前值会被用作函数的返回值。

2、优势:
这些命名并不是必须的,但它们可以使代码更简短、更清晰。事实上,这些命名本身就像文档,帮助读者理解每个返回值的意义。例如,在 nextInt 函数中,通过命名返回值,我们可以很清楚地知道每个返回的整数是什么。

3、简化和明确:
由于命名的结果已经初始化并与一个没有装饰的 return 绑定,因此它们可以使代码更简单,也更易于理解。例如,ReadFull 函数的版本就很好地利用了这个特性。在这个函数中,n 和 err 是命名的返回值。在函数体内,不需要明确指定要返回的值,因为在执行 return 时,n 和 err 的当前值会自动被返回。

简而言之,命名的返回参数可以提高代码的可读性,并简化 return 语句的编写,因为你不再需要明确指定返回哪些值。

Defer

Go's defer statement schedules a function call (the deferred function) to be run immediately before the function executing the defer returns. It's an unusual but effective way to deal with situations such as resources that must be released regardless of which path a function takes to return. The canonical examples are unlocking a mutex or closing a file.

// 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.
}

Deferring a call to a function such as Close has two advantages. First, it guarantees that you will never forget to close the file, a mistake that's easy to make if you later edit the function to add a new return path. Second, it means that the close sits near the open, which is much clearer than placing it at the end of the function.

The arguments to the deferred function (which include the receiver if the function is a method) are evaluated when the defer executes, not when the call executes. Besides avoiding worries about variables changing values as the function executes, this means that a single deferred call site can defer multiple function executions. Here's a silly example.

for i := 0; i < 5; i++ {
    defer fmt.Printf("%d ", i)
}

Deferred functions are executed in LIFO order, so this code will cause 4 3 2 1 0 to be printed when the function returns. A more plausible example is a simple way to trace function execution through the program. We could write a couple of simple tracing routines like this:

func trace(s string)   { fmt.Println("entering:", s) }
func untrace(s string) { fmt.Println("leaving:", s) }

// Use them like this:
func a() {
    trace("a")
    defer untrace("a")
    // do something....
}

We can do better by exploiting the fact that arguments to deferred functions are evaluated when the defer executes. The tracing routine can set up the argument to the untracing routine. This example:

func trace(s string) string {
    fmt.Println("entering:", s)
    return s
}

func un(s string) {
    fmt.Println("leaving:", s)
}

func a() {
    defer un(trace("a"))
    fmt.Println("in a")
}

func b() {
    defer un(trace("b"))
    fmt.Println("in b")
    a()
}

func main() {
    b()
}

prints

entering: b
in b
entering: a
in a
leaving: a
leaving: b

For programmers accustomed to block-level resource management from other languages, defer may seem peculiar, but its most interesting and powerful applications come precisely from the fact that it's not block-based but function-based. In the section on panic and recover we'll see another example of its possibilities.
这段文本详细介绍了 Go 语言中的 defer 语句,它的工作原理以及如何使用它。以下是对这部分内容的详细解释:

基本概念:

defer: 在 Go 中, defer 语句被用于安排一个函数调用(被称为延迟函数)在包含 defer 的函数返回前立即执行。
1、为何使用 defer:

defer 常用于处理必须在函数结束时执行的操作,无论函数因哪个路径返回。典型的例子是释放资源,例如解锁互斥锁或关闭文件。
例子:Contents 函数:

这个函数读取一个文件的内容并返回它。文件被打开后,使用 defer f.Close() 确保文件在函数返回时被关闭。
使用 defer 的好处是:1) 你永远不会忘记关闭文件;2) Close 函数的调用靠近 Open,使代码更清晰。

2、defer 和参数:

func TestDefer(t *testing.T) {
    var i int = 100
    defer func() {
        fmt.Println(i)
    }()
    i = i + 1
    return
}

func TestDefer1(t *testing.T) {
    var i int = 100
    defer func(i int) {
        fmt.Println(i)
    }(i)
    i = i + 1
    return
}

延迟函数的参数(包括接收者,如果函数是方法)在 defer 语句执行时被求值,而不是在延迟函数调用时。当你写下一个 defer 语句时,该语句中的任何参数都会立即被计算和固定,不会等到实际延迟函数被调用时才计算。

延迟的函数按照 LIFO(后进先出)的顺序执行。

跟踪函数执行:

3、通过使用 trace 和 untrace 函数,可以跟踪函数何时进入和退出。另一个例子中,trace 和 un 通过返回值和参数进行了优化,以更明确地表示函数的进入和退出。

总结:

对于习惯于其他语言中基于块级资源管理的程序员,defer 可能看起来有些奇怪。但其实,defer 最有趣和强大的应用恰恰是因为它基于函数而不是基于块。
简而言之,defer 在 Go 中是一个强大的工具,用于确保在函数返回前执行某些操作,特别是资源的释放。这种机制提供了一个清晰、简洁的方法来处理资源管理和其他需要在函数退出时执行的任务。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,287评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,346评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,277评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,132评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,147评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,106评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,019评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,862评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,301评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,521评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,682评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,405评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,996评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,651评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,803评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,674评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,563评论 2 352

推荐阅读更多精彩内容