go的函数式编程

defer的特点

  1. defer栈是后进先出,可以穿越panic和return
  2. 参数在defer语句运行时计算
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

func deferVal() {
for i := 0; i < 3; i++ {
fmt.Println(i)
defer func() { fmt.Println(i) }()
}
}

func writeFile(filename string) {
file, err := os.OpenFile(filename, os.O_EXCL|os.O_CREATE, 0666)
if err != nil {
//fmt.Println("file already exist")
if pathError, ok := err.(*os.PathError); !ok {
panic(err)
} else {
fmt.Println(pathError.Op, pathError.Path, pathError.Err)
}
return
}
defer file.Close()

writer := bufio.NewWriter(file)
defer writer.Flush()

//f := fib.Fib()
var i int = 0
for ; i < 3; i++ {
// 打印3遍
fmt.Fprintf(writer, "你好啊")
}
}


func main() {
// 创建文件 ./fib.txt
writeFile("fib.txt") // 你好啊你好啊你好啊

deferVal() // 0 1 2 3 3 3
}

http的错误处理

自定义错误、自定义结构体实现接口

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
type custom_error struct {
content string
time time.Time
position string
}

type userError string

func (r userError) Error() string {
return r.Message()
}
func (r userError) Message() string {
return string(r)

}

func (e custom_error) Error() string {
//TODO implement me
return fmt.Sprintf(" %s : %s", e.position, e.content)
}

func judgeCustomErr(err error) bool {
switch err.(type) {
case custom_error:
return true
default:
return false
}
}

const prefix = "/source/"

func fileHandler(writer http.ResponseWriter, request *http.Request) error {
if strings.Index(request.URL.Path, prefix) != 0 {
return userError("path must start with " + prefix)
}
path := request.URL.Path[len(prefix):]
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()

//// 随机自定义错误
//a, b := rand.Intn(400), 200
//if a < b {
// return custom_error{"错误自定义", time.Now(), "这里是错误"}
//}

all, err := ioutil.ReadAll(file)
if err != nil {
return err
}

writer.Write(all)
return nil
}

type appHandler func(writer http.ResponseWriter, request *http.Request) error

func errWraper(handler appHandler) func(writer http.ResponseWriter, request *http.Request) {
return func(writer http.ResponseWriter, request *http.Request) {
defer func() {
if r := recover(); r != nil {
log.Printf("process error: %v", r)
http.Error(writer, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
}
}()

err := handler(writer, request)
if err != nil {
if userErr, ok := err.(userError); ok {
http.Error(writer, userErr.Message(), http.StatusInternalServerError)
}

code := http.StatusOK
r, ok := err.(custom_error)
log.Printf("err occured during handling open file: %s ", err.Error())
switch {
case os.IsNotExist(err):
code = http.StatusNotFound
case os.IsPermission(err):
code = http.StatusForbidden
case ok:
http.Error(writer, r.Error(), 500)
default:
code = http.StatusInternalServerError
}
if !judgeCustomErr(err) {
http.Error(writer, http.StatusText(code), code)
}
}
}
}

func main(){

http.HandleFunc("/source/", errWraper(fileHandler))

err := http.ListenAndServe(":8888", nil)
if err != nil {
panic(err)
}
}

panic 和 recover

error
意料之中的错误使用error;如:文件打不开
意料之外的错误使用panic;如:数组越界

panic

  1. 停止当前函数执行
  2. 一直向上返回,执行每一层的defer
  3. 如果没有遇见recover,程序退出

recover

  1. 仅在defer中调用
  2. 获取panic的值
  3. 如果无法处理,重新panic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

func tryRecover() {
defer func() {
r := recover()
if err, ok := r.(error); ok {
fmt.Println("error occured:", err)
} else {
panic(err)
}
}()

b := 0
a := 5 / b
fmt.Println(a)

//panic(errors.New("this is a error"))
}