5. go基础:通过 error/deffer/panic 处理错误

正确处理错误,可以让程序更加稳定。error是一个错误接口,一般来说,error是不会影响到整个程序运行的,我们可以自行处理。 而panic是会导致程序直接崩溃退出的,我们也可以用 go自带的recover()来恢复panic,在程序崩溃前进行资源的释放工作。 错误 error接口 error接口只有一个Error方法,返回具体的错误信息(string) type error interface { Error() string } 一个字符串转整数的例子: func main(){ // try to conver alpha "a" to integer i, err := strconv.Atoi("a") if err != nil { fmt.Println(err) } else { fmt.Println(i) } } 尝试将字符 a 转换成为 整数,显然是不成功的,因此返回了err 一般而言,error 接口用于当方法或者函数执行遇到错误时进行返回,而且是第二个返回值。通过这种方式,可以让调用者自己根据错误信息决定如何进行下一步处理。 error 工厂函数 除了可以使用其他函数,自己定义的函数也可以返回错误信息给调用者 自定义error 自定义error就是先自定义一个新的类型,然后实现error接口 // 自定义一个结构体,携带错误码和具体的错误信息 type commonError struct { errorCode int // 错误码 errorMsg string // 错误信息 } // 实现error接口 func (ce *commonError) Error() string { return ce.errorMsg } error 断言 func add(a, b int) int, error { if a < 0 || b < 0 { return 0, &commonError{ errorCode: 1, errorMsg: "a and b must >= 0" } } return a + b, nil } func main(){ sum, err := add(-1, 2) // 错误类型断言 if cm, ok := err.(*commonError); ok { fmt.Printf("Error code = %d, error message = %s\n", cm.errorCode, cm.errorMsg) } else { fmt.Printf("Sum = %d\n", sum) } } 错误嵌套 Error Wrapping 需求,将一个错误嵌套到另一个错误中: ...

<span title='2022-02-17 00:00:00 +0000 UTC'>February 17, 2022</span>&nbsp;·&nbsp;2 min&nbsp;·&nbsp;LingZihuan

3. go基础:函数和方法

函数和方法,大大提高了我们的代码复用率,也提高了代码的可读性。 go语言的函数和方法的定义十分简单,直接使用 func 关键字即可。 不同于python、C++等有类的概念,而这些类中,又有自己的方法,或者父类的方法。go语言中,我们可以定义一个数据结构,然后给这个数据结构实现自己的方法,同样可以实现【类】的效果,通过组合,同一个结构体中,也可以使用或者覆写其他结构体的方法。 函数 函数的组成: 关键字 func 函数名 funcName 参数 params 返回值(返回值类型) 函数体 func funcName(params) result { // body } // 示例1:普通的函数定义 func sum(a int, b int) int { return a + b } // 示例2:参数表的类型合并,(a int, b int) -> (a, b int) func sum2(a, b int) int { return a + b } // 示例3:多值返回,用 () 包裹起来 func sum3(a, b int) (int, error) { if a < 0 || b < 0 { return 0, errors.New("invalid params") } return a + b, nil } // 示例4:命名返回参数 // 这里命名的返回值是 res 和 err // 我们可以在函数体中直接使用这两个变量 func sum4(a, b int) (res int, err error) { if a < 0 || b < 0 { // 可以直接返回 return 0, errors.New("invalid params") } // 也可以给参数赋值,然后return res = a + b err = nil return } 可变参数 // 这里的可变参数类型是一个切片 func sum5(params ...int) int { res := 0 for _, num := range params { res += num } return res } 需要注意的是,当函数中有普通参数,也有可变参数的时候,那么可变参数一定是放在参数表的最后的。 ...

<span title='2022-02-15 00:00:00 +0000 UTC'>February 15, 2022</span>&nbsp;·&nbsp;2 min&nbsp;·&nbsp;LingZihuan

4. go基础:结构体和接口

接口是一种聚合类型,结构体是和调用方的一种约定,有点抽象类的意思。:) 结构体 结构体定义 结构体是一种聚合类型,里面可以包含任意类型的值,这些值就是我们定义的结构体成员,也称为字段 在go语言中,要定义一个结构体,需要使用 type+struct 关键字组合。 // 定义一个代表【人】的结构体 type person struct { name string // 名称 age uint // 年龄 } 结构体的成员字段并不是必需的,也可以一个字段都没有,这种结构体成为空结构体。 type s struct {} 结构体声明使用 // 使用var声明一个person变量,未初始化,里面的值为各自变量的零值 var p person // 可以使用结构体字面量初始化的方式 p2 := person{"Mike", 10} // 第一个值为 name,第二个值为age,与结构体字段定义顺序有关 // 可以指定字段名初始化,不按定义顺序 p3 := person{age: 10, name: "Mike"} 字段结构体 type address struct { province string city string } // 结构体的字段可以是任意类型,也可以是自定义的结构体 type person struct { name string age uint addr address } // 初始化 p := person{ name: "Mike", age: 10, addr: address{ province: "Guandong", city: "Maoming", }, } fmt.Println(p.addr.province) 接口 接口的定义 接口是和调用方的一种约定,是一个高度抽象的类型,不需要和具体的实现细节绑定在一起。 接口要做的就是定义好约定,告诉调用方,自己可以做什么,但是不需要知道它的内部是如何实现的 我们通过 type + interface关键字定义一个接口 ...

<span title='2022-02-15 00:00:00 +0000 UTC'>February 15, 2022</span>&nbsp;·&nbsp;3 min&nbsp;·&nbsp;LingZihuan

2. go基础:使用array/slice/map

数组,大概是每个变成语言都有基础数据结构。 Go的切片类型(slice)提供了一种方便和有效的方法来处理类型化数据序列(数组)。slice类似于其他语言中的数组,但有一些不同寻常的属性。 map可以理解为一个字典。 数组声明 可以使用 [长度]类型{} 来定义,比如 [2]string{}定义一个长度为2的字符串数组 这里的长度必须是一个常量,如果要使用变量去动态生成数组的话,需要使用make,如 make([]string, size) 这里的size是一个变变量,生成size长度的字符串数组 另外需要注意的是, [1]string 和 [2]string 不是用一种类型,长度也是数组类型的一部分 定义数组时可以省略数组的长度,此时会根据大括号{}里面的元素个数推到出数组的长度: arr := []string{"a", "b", "c", "d", "e"} 这里会自动生成长度为5的字符串数组,适用于所有元素都被初始化的数据 但是如果只有特定元素被初始化的数组,就不合适了: arr := [5]string{1: "a", 3: "c"} 上述表示数组初始化索引为1的值为a,索引为3的值为c,其他未初始化的为空值(字符串的空值为"") 如果我们不指定 [5]string,那么go就会根据我们定义的最大索引值3 来确定数组的长度为4 数组循环 使用传统的数组循环: for i := 0; i < len(arr); i++ { fmt.Println(arr[i]) } 使用 for range 数组遍历,写法更加简洁 for index, value := range arr { fmt.Printf("index = %v, value = %v\n", index, value) } 切片 Slice 切片的区间是左闭右开,如arr[2:5],选取的索引取数是[2,3,4] 切片的底层是(原)数组,将切片内容修改之后,原数组也会改变 arr := []int{1,2,3,4,5} sliced := arr[1:3] // sliced = {2,3} sliced[0] = 0 // 切片修改后,原数组内容有会改变 // arr = {1, 0, 3, 4, 5} 使用make声明切片 声明一个元素类型为string的切片,长度为5:slice1 := make([]string, 5) 还可以传入一个容量参数:slice2 := make([]string, 4, 8) 这里声明了一个长度为4,容量为8的字符串数组 长度是切片内元素的个数,容量是切片的空间 ...

<span title='2022-02-10 00:00:00 +0000 UTC'>February 10, 2022</span>&nbsp;·&nbsp;2 min&nbsp;·&nbsp;LingZihuan

1. go基础:数据类型,以及控制结构

go最近很火啊,自带高并发,内置网络模块强大,作为后端工程师一枚,自然也要学起来! go 的基础部分跟我们之前学过的大部分语言是都是十分相似的,上手十分简单。 第一个go程序 第一个程序大都是hello嘛,向新世界打个招呼 :) import fmt func main(){ fmt.Println("hello golang!") } 数据类型以及变量声明的方式 变量声明的方式 // var 变量名 类型 = 表达式 var num int = 1 // 也可以声明变量,但是不赋值 var num int // 这样的默认值就是int的默认值 0 // 可以直接赋值并且声明类型 num := 1 // 可以实现多值定义 a, b, c := 1, 2, "hello" var ( a = 1 b = 2 c = "hello" ) 常量声明 常量声明,我们可以直接使用 const 关键字,不需要指定数据的类型,通常,常量我们用大写进行命名 我们指定变量的值,会自定将变量转为对应的类型 const MAX_SIZE = 10 基础类型 整型 有符号:int, int8, int16, int32, int64 无符号:uint,uint8,uint16,uint32,uint64 浮点数:float,float32, float64 布尔型:bool 字符串:string 指针 常量:使用const声明 const name = "Mike" 字符串转换 字符串转数字: strconv.Atoi("123") 数字转字符串:strconv.Itoa(123) 数字类型的强制转换: ...

<span title='2022-01-07 00:00:00 +0000 UTC'>January 7, 2022</span>&nbsp;·&nbsp;2 min&nbsp;·&nbsp;LingZihuan