Go - panic
原则 不要相信任何外部输入的参数 函数需要对所有输入的参数进行合法性的检查 一旦发现问题,立即终止函数的执行,返回预设的错误值 不要忽略任何一个错误 显式检查这些函数调用返回的错误值 一旦发现错误,要及时终止函数执行,防止错误继续传播 不要假定异常不会发生 异常不是错误 错误是可预期的,也会经常发生,有对应的公开错误码和错误处理方案 异常是不可预期的,通常指的是硬件异常、操作系统异常、语言运行时异常、代码 Bug(数组越界访问)等 异常是小概率事件,但不能假定异常不会发生 根据函数的角色和使用场景,考虑是否要在函数内设置异常捕获和恢复的环节 panic 在 Go 中,由 panic 来表达异常的概念 panic 指的是 Go 程序在运行时出现的一个异常情况 如果异常出现了,但没有被捕获并恢复,则 Go 程序的执行会被终止 即便出现异常的位置不在主 goroutine panic 来源:Go 运行时 / 开发者通过 panic 函数主动触发 当 panic 被触发,后续的执行过程称为 panicking 手动调用 panic 函数,主动触发 panickin...
Go - error
C vs Go Go 语言的错误处理机制是在 C 语言错误处理机制基础上的再创新 通常使用类型为整型的函数返回值作为错误状态标识,函数调用者会基于值比较的方式来处理错误 返回值为 0,代表函数调用成功,否则函数调用出现错误 优点 要求开发者必须显式地关注和处理每个错误 错误一般为普通值,不需要额外的语言机制来处理,让代码更容易调试 C 语言错误处理机制具有简单和显式结合的特征,非常符合 Go 的设计哲学 因此 Go 继承了 C 的错误处理机制 缺点 C 语言中的函数最多仅支持一个返回值 一值多用 承载函数要返回给调用者的信息 承载函数调用的最终错误状态 当返回值为其它类型,如字符串,很难将数据与错误融合在一起 做法不一,很难形成统一的错误处理策略 因此 Go 函数新增了多返回值机制,用于支持数据与错误(返回值列表的末尾)的分离 fmt/print.go123456789// Fprintf formats according to a format specifier and writes to w.// It returns the number of bytes w...
Go - Function
声明 关键字 func Go 函数声明必须以关键字 func 开始 函数名 在同一个 Go 包中,函数名应该是唯一的 遵循 Go 标识符的导出规则 参数列表 Go 函数支持变长参数,即一个形式参数对应多个实际参数 返回值列表 支持具名返回值,比较少用 函数体 可选,如果没有函数体,说明该函数可能在 Go 语言之外实现的 可能使用汇编语言,然后通过链接器将实现与声明中的函数名链接在一起 类比 将函数声明等价转换为变量声明的形式 - 声明一个类型为函数类型的变量 Key Value 变量名 函数名 函数签名 参数列表 + 返回值列表 函数类型 func + 参数列表 + 返回值列表func + 函数签名 函数签名:决定两个函数类型是否相同 在表述函数类型时,通常会省略函数签名参数列表中的参数名,以及返回值列表中的返回值变量名 12// 函数类型,只关注入参和出参func (io.Writer, string, ...interface{}) (int, error) 相同的函数签名,相同的函数类型 12345678func...
Go - switch
语法12345678910switch initStmt; expr { case expr1: // 分支 1 case expr2_1, expr2_2: // 分支 2 case exprN: // 分支 N default: // 默认分支} 顺序 按照定义顺序(从上到下,从左到右),先到先得,匹配后中断 123456789101112131415161718192021222324252627282930313233343536373839404142434445package mainfunc case1() int { println("eval case1") return 1}func case2_1() int { println("eval case2_1") return 0}func case2_2() int { println("eval case2_2") ...
Go - for
经典C1234567891011#include <stdio.h>int main() { int i; int sum = 0; for (i = 0; i < 10; i++) { sum += i; } printf("%d\n", sum);} Go i 为循环变量,作用域仅限于 for 语句的隐式代码块范围内 123456789package mainfunc main() { var sum int for i := 0; i < 10; i++ { sum += i } println(sum)} 多变量123456789package mainfunc main() { var sum int for i, j, k := 0, 1, 2; (i < 10) && (j < 20) && (k < 2...
Go - if
概述 if 语句是 Go 语言中提供的一种分支控制结构,根据布尔表达式的值,在两个分支中选择一个执行 12345if boolean_expression { // 新分支}// 原分支 二分支结构 12345if boolean_expression { // 分支 1} else { // 分支 2} 多分支结构 123456789if boolean_expression1 { // 分支 1} else if boolean_expression2 { // 分支 2} else if boolean_expression3 { // 分支 3} else { // 分支 4} 12345678910111213if boolean_expression1 { // 分支 1} else { if boolean_expression2 { ...
Go - Struct
新类型类型定义 Type Definition,基于已有类型 1type T S // S 为任意的已定义类型,包括原生类型 新类型 T1 是基于原生类型 int 定义的新类型,而新类型 T2 则是基于刚定义的类型 T1 定义的新类型 12type T1 inttype T2 T1 底层类型 如果一个新类型是基于某个原生类型定义的,那么该原生类型为新类型的底层类型(Underlying Type) 如果一个新类型不是基于原生类型定义的,那么就就递归查找 12type T1 int // T1 的底层类型为 inttype T2 T1 // T2 基于 T1 创建,而 T1 的底层类型为 int,所以 T2 的底层类型也为 int 底层类型用来判断两个类型本质上是否相同(identical) 本质相同的两个类型,它们的变量可以通过显式转型进行相互赋值 本质不同的两个类型,变量之间都无法显式转型 1234567891011121314type T1 inttype T2 T1 // T2 与 T1 本质相同type T3 string // T3 与 T1 本质不同,无法...
Go - Map
定义 map 表示一组无序的键值对,map 中每个 Key 都是唯一的 Key 和 Value 的类型不要求相同,如果 Key 类型和 Value 类型都相同,map 类型才是等价的 12345m1 := map[string]string{}m2 := make(map[int]string)fmt.Printf("%T\n", m1) // map[string]stringfmt.Printf("%T\n", m2) // map[int]string 对 Value 类型没有限制,但对 Key 类型有严格限制因为 map 类型要保证 Key 类型的唯一性,因此,Key 类型必须支持 == 和 != 两种比较操作 在 Go 中, slice、map、func都只支持与 nil 比较,并不支持同类型变量比较,因此不能作为 map 类型的 Key 1234567891011s1 := make([]int, 1)s2 := make([]int, 1)fmt.Println(s1 == s2) // invalid op...
Go - Array + Slice
数组逻辑定义 Go 数组:一个长度固定,由同构类型元素组成的连续序列 123// 声明了一个数组变量 arr,其类型为 [5]int,其中元素的类型为 int,数组的长度为 5var arr [5]int = [5]int{1, 2, 3, 4, 5}fmt.Println(arr) // [1 2 3 4 5] 数组元素的类型可以为任意 Go 原生类型或者自定义类型 数组的长度必须在声明数组变量时提供 Go 编译器需要在编译阶段就知道数组类型的长度(整型数字字面值 or 常量表达式) 如果两个数组类型的元素类型 T 和数组长度 N 都是一样的,那么两个数组类型是等价的 12345678910111213141516func f(arr [5]int) {}func main() { var arr1 [5]int var arr2 [6]int var arr3 [5]string fmt.Printf("%T\n", arr1) // [5]int fmt.Printf("...
Go - Constant
原生概述 Go 常量是一种在源码编译期间被创建的语法元素 该元素的值可以像变量那么被初始化,但初始化表达式必须在编译期可以求值 Go 常量一旦声明并被初始化,它的值在整个程序的生命周期内便保持不变 在并发设计时不需要考虑常量访问的同步 并且被创建并初始化后的常量可以作为其它常量的初始化表达式的一部分 Go 引入 const 关键字声明常量,与 var 类似 123456const Pi float64 = 3.14159265358979323846const ( size int64 = 1024 i, j, s = 1, 2, "str") Go 常量的类型仅限于基本数据类型:数值类型、字符串类型、布尔类型 C C 语言原生不支持常量 在 C 语言中,字面值担负着常量的角色 为了不让字面值以魔数的形式分布于源码各处,早期 C 语言的常用实践是使用宏(macro),即宏定义常量 使用宏定义常量一直是 C 编码中的主流风格,即便后续的 C 标准中提供了 const 关键字 123#define FILE_MAX_LEN 0x...














