18、孰能无过-GOLANG

处理错误

go语言支持多个返回值,按照惯例我们把错误放在最后一个返回值返回

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main

import (
"fmt"
"io/ioutil"
"os"
)

func main() {
files, error := ioutil.ReadDir(".")//函数调用成功error为nil,if判断就不会生效,如果调用失败程序打印错误后立即退出
if error != nil {
fmt.Println(error)
os.Exit(1)
}
for _, value := range files {
fmt.Println(value.Name())//打印当前目录的所有文件名
}
}

image-20230312093318268

image-20230312093332823

尝试读取一个虚构文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package main

import (
"fmt"
"io/ioutil"
"os"
)

func main() {
files, error := ioutil.ReadDir("nullfile.txt")
if error != nil {
fmt.Println(error)
os.Exit(1)
}
for _, value := range files {
fmt.Println(value.Name())
}
}

image-20230312093735797

优雅的处理错误

文件写入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package main

import (
"fmt"
"os"
)

func proverbs(name string) error {
f, err := os.Create(name)//创建一个文件
if err != nil {
return err
}

_, err = fmt.Fprintln(f, "Errors are values.")//为文件写入内容
if err != nil {
f.Close()
return err
}

_, err = fmt.Fprintln(f, "Don`t just check errors,handle them gracefully.")
f.Close()
return err
}
func main() {
err := proverbs("1.txt")
if err != nil {
fmt.Println(err)
os.Exit(1)
}
}

image-20230312100806061上面这种错误处理方式看着逻辑很清晰,但是充斥着大量的错误判断的语句,使代码整体结构变得复杂

关键字defer

如果一个函数在内部使用了defer延迟了某些操作,那么go语言将保证这些被延迟的操作会在函数返回之前触发,如下面的例子,出现在关键字之后的每一个return语句都会导致f.Close(),方法被调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package main

import (
"fmt"
"os"
)

func proverbs(name string) error {
f, err := os.Create(name)
if err != nil {
return err
}
defer f.Close()

_, err = fmt.Fprintln(f, "Errors are values.")
if err != nil {
return err
}

_, err = fmt.Fprintln(f, "Don`t just check errors,handle them gracefully.")
return err
}
func main() {
err := proverbs("1.txt")
if err != nil {
fmt.Println(err)
os.Exit(1)
}
}
//该程序与文件写入的例子实现的效果一模一样

创造性的错误处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
package main

import (
"fmt"
"io"
"os"
)

type safeWriter struct {
w io.Writer
err error
}

func (sw *safeWriter) writeln(s string) {
if sw.err != nil {//如果有错误直接跳过写入
return
}
_, sw.err = fmt.Fprintln(sw.w, s)
}

func proverbs(name string) error {
f, err := os.Create(name)
if err != nil {
return err
}
defer f.Close()

sw := safeWriter{w: f}
sw.writeln("Errors are values.")
sw.writeln("Don’t just check errors, handle them gracefully.")
sw.writeln("Don't panic.")
sw.writeln("Make the zero value useful.")
sw.writeln("The bigger the interface, the weaker the abstraction.")
sw.writeln("interface{} says nothing.")
sw.writeln("Gofmt's style is no one's favorite, yet gofmt is everyone's favorite.")
sw.writeln("Documentation is for users.")
sw.writeln("A little copying is better than a little dependency.")
sw.writeln("Clear is better than clever.")
sw.writeln("Concurrency is not parallelism.")
sw.writeln("Don’t communicate by sharing memory, share memory by communicating.")
sw.writeln("Channels orchestrate; mutexes serialize.")

return sw.err

}

func main() {
err := proverbs("proverbs.txt")
if err != nil {
fmt.Println(err)
os.Exit(1)
}
}

image-20230312102619441

新的错误

当函数接收到不正确的形参,你可以创建并返回新的错误来通知调用者出现了什么问题

编写一个9*9的网格

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package main

import (
"errors"
"fmt"
)

const rows, columns = 9, 9

type Grid [rows][columns]int8

func (g Grid) Set(row, column int, digit int8) error {
if !inBounds(row, column) {
return errors.New("下标越界")
}
g[row][column] = digit
return nil
}
func inBounds(row, column int) bool { //检查是否超出9*9的范围
if row < 0 || row >= rows {
return false
}
if column < 0 || column >= columns {
return false
}
return true
}

func main() {
var g Grid
err := g.Set(10, 0, 5)
if err != nil {
fmt.Printf("An error occurred:%v\n", err)
}
}

image-20230312132441720

不要惊恐

go语言有一种类似于异常的机制,panic,panic会在退出程序前执行所有被延迟的操作,而os.Exit()则不会这么做

1
2
3
4
5
6
7
8
9
10
11
12
13
package main

import "fmt"

func main() {
defer func() {
if e := recover(); e != nil { //在正常的执行过程中,调用recover会返回nil
fmt.Println(e)
}
}()
panic("i forget my dogs!")

}

image-20230312133755598