欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 健康 > 养生 > Go基础之条件语句,For循环,错误处理

Go基础之条件语句,For循环,错误处理

2025/2/24 0:50:23 来源:https://blog.csdn.net/u012060033/article/details/144477404  浏览:    关键词:Go基础之条件语句,For循环,错误处理

文章目录

  • 1 条件语句
    • 1.1 if 语句
    • 1.2 switch
      • 1.2.1 switch
      • 1.2.2 Type Switch
      • 1.2.3 fallthrough
  • 2 for循环
    • 2.1 简介
    • 2.2 For-each range 循环
    • 2.3 示例
  • 3 错误处理
    • 3.1 简介
    • 3.2 error 接口
      • 3.2.1 error接口
      • 3.2.2 使用 errors 包创建错误
      • 3.2.3 errors 包原理
    • 3.3 显式返回错误
    • 3.4 自定义错误
      • 3.4.1 自定义
      • 3.4.2 使用 errors.Is 和 errors.As
    • 3.5 panic 和 recover
      • 3.5.1 简介
      • 3.5.2 示例
      • 3.5.3 recover 恢复后获得堆栈跟踪

1 条件语句

1.1 if 语句

if 语句使用特点:

  • 不需使用 小括号() 将条件包含起来
  • 大括号{}必须存在,即使只有一行语句
  • 左括号必须在if或else的同一行
  • 在if之后,条件语句之前,可以添加变量初始化语句,使用 ; 进行分隔
  • 在有返回值的函数中,最终的 return 不能在条件语句中

Go 编程语言中 if 语句的语法如下:

if 布尔表达式 {/* 在布尔表达式为 true 时执行 */
} else if 布尔表达式 {/* 在布尔表达式为 false 时执行 */
}else {/* 在布尔表达式为 false 时执行 */
}
或者
if statement; condition {  
}

例如:

 if a < 20 {/* 如果条件为 true 则执行以下语句 */fmt.Printf("a 小于 20\n" )}

如果在条件判断语句里面允许声明一个变量,这个变量的作用域只能在该条件逻辑块内,其他地方就不起作用了,如下所示:

package mainimport "fmt"
func main() {if num := 9; num < 0 {fmt.Println(num, "is negative")} else if num < 10 {fmt.Println(num, "has 1 digit")} else {fmt.Println(num, "has multiple digits")}
}

1.2 switch

1.2.1 switch

switch 语句用于基于不同条件执行不同动作,每一个 case 分支都是唯一的,从上至下逐一测试,直到匹配为止。
switch 语句执行的过程 从上至下,直到找到匹配项,匹配项后面也不需要再加 break。因为 switch 默认情况下 case 最后自带 break 语句,匹配成功后就不会执行其他 case,如果我们需要执行后面的 case,可以使用 fallthrough
switch 的 default 不论放在哪都是最后执行,不会受位置影响
Go 编程语言中 switch 语句的语法如下:

switch var1 {case val1:...case val2:...default:...
}

示例

package mainimport "fmt"
func main() {/* 定义局部变量 */var grade string = "B"var marks int = 90switch marks {case 90: grade = "A"case 80: grade = "B"case 50,60,70 : grade = "C"default: grade = "D"  }
switch 中的表达式是可选的,可以省略。
如果省略表达式,则相当于 switch true,这种情况下会将每一个 case 的表达式的求值结果与 true 做比较,如果匹配,则执行相应的代码块switch {case grade == "A" :fmt.Printf("优秀!\n" )     case grade == "B", grade == "C" :fmt.Printf("良好\n" )      case grade == "D" :fmt.Printf("及格\n" )      case grade == "F":fmt.Printf("不及格\n" )default:fmt.Printf("差\n" );}fmt.Printf("你的等级是 %s\n", grade );      
}

可以在一个 case 中包含多个表达式,每个表达式用逗号分隔。

letter := "i"switch letter { case "a", "e", "i", "o", "u": //multiple expressions in casefmt.Println("vowel")default:fmt.Println("not a vowel")}

1.2.2 Type Switch

switch 语句还可以被用于 type-switch 来判断某个 interface 变量中实际存储的变量类型。

实例

package mainimport "fmt"func main() {var x interface{}switch i := x.(type) {case nil:  fmt.Printf(" x 的类型 :%T",i)                case int:  fmt.Printf("x 是 int 型")                      case float64:fmt.Printf("x 是 float64 型")          case func(int) float64:fmt.Printf("x 是 func(int) 型")                      case bool, string:fmt.Printf("x 是 bool 或 string 型" )      default:fmt.Printf("未知型")    }  
}
以上代码执行结果为:x 的类型 :<nil>

1.2.3 fallthrough

使用 fallthrough 会强制执行后面的 case 语句,fallthrough 不会判断下一条 case 的表达式结果是否为 true。

package mainimport "fmt"func main() {switch {case false:fmt.Println("1、case 条件语句为 false")fallthroughcase true:fmt.Println("2、case 条件语句为 true")fallthroughcase false:fmt.Println("3、case 条件语句为 false")fallthroughcase true:fmt.Println("4、case 条件语句为 true")case false:fmt.Println("5、case 条件语句为 false")fallthroughdefault:fmt.Println("6、默认 case")}
}结果为:2case 条件语句为 true
3case 条件语句为 false
4case 条件语句为 true

2 for循环

2.1 简介

Go 语言的 For 循环有 3 种形式,只有其中的一种使用分号。

和 C 语言的 for 一样:
for init; condition; post { }
init: 一般为赋值表达式,给控制变量赋初值;
condition: 关系表达式或逻辑表达式,循环控制条件;
post: 一般为赋值表达式,给控制变量增量或减量。和 C 的 while 一样:
for condition { }和 C 的 for(;;) 一样:
for { }

示例:

for i := 1; i <= 10; i++ { fmt.Printf(" %d", i)
}

无限循环示例:

for {fmt.Println("Hello World")}

也可以在for循环中声明和操作多个变量。让我们编写一个程序,使用多个变量声明:

for no, i := 10, 1; i <= 10 && no <= 19; i, no = i+1, no+1 {//multiple initialisation and incrementfmt.Printf("%d * %d = %d\n", no, i, no*i)
}

2.2 For-each range 循环

for 循环的 range 格式可以对 slice、map、数组、字符串等进行迭代循环。
格式如下:

for key, value := range oldMap {newMap[key] = value
}
如果只想读取 key,格式如下:
for key := range oldMap或者这样:
for key, _ := range oldMap如果只想读取 value,格式如下:
for _, value := range oldMap

在遍历时可以使用 _ 来忽略索引或值。

package mainimport "fmt"func main() {nums := []int{2, 3, 4}// 忽略索引for _, num := range nums {fmt.Println("value:", num)}// 忽略值for i := range nums {fmt.Println("index:", i)}
}

2.3 示例

package main
import "fmt"func main() {strings := []string{"google", "runoob"}for i, s := range strings {fmt.Println(i, s)}numbers := [6]int{1, 2, 3, 5}for i,x:= range numbers {fmt.Printf("第 %d 位 x 的值 = %d\n", i,x)}  
}

for 循环内对同名变量继续声明赋值

package mainimport "fmt"func main(){var a int = 0fmt.Println("for start")for a:=0; a < 10; a++ {fmt.Println(a)}fmt.Println("for end")fmt.Println(a)
}

在 for 循环的 initialize(a:=0) 中,此时 initialize 中的 a 与外层的 a 不是同一个变量,initialize 中的 a 为 for 循环中的局部变量,因此在执行完 for 循环后,输出 a 的值仍然为 0。

3 错误处理

3.1 简介

Go 通过内置的错误接口提供了非常简单的错误处理机制。
Go 语言的错误处理采用显式返回错误的方式,而非传统的异常处理机制。这种设计使代码逻辑更清晰,便于开发者在编译时或运行时明确处理错误。

Go 的错误处理主要围绕以下机制展开:

  • error 接口:标准的错误表示。
  • 显式返回值:通过函数的返回值返回错误。
  • 自定义错误:可以通过标准库或自定义的方式创建错误。
  • panic 和 recover:处理不可恢复的严重错误。

3.2 error 接口

3.2.1 error接口

Go 标准库定义了一个 error 接口,表示一个错误的抽象。
error 类型是一个接口类型,这是它的定义:

type error interface {Error() string
}

任何实现了 Error() 方法的类型都可以作为错误,Error() 方法返回一个描述错误的字符串。

3.2.2 使用 errors 包创建错误

我们可以在编码中通过实现 error 接口类型来生成错误信息。

创建一个简单错误:

package mainimport ("errors""fmt"
)func main() {err := errors.New("this is an error")fmt.Println(err) // 输出:this is an error
}

函数通常在最后的返回值中返回错误信息,使用 errors.New 可返回一个错误信息:

func Sqrt(f float64) (float64, error) {if f < 0 {return 0, errors.New("math: square root of negative number")}// 实现
}

3.2.3 errors 包原理

接口 error 的作用是为错误值提供一个标准的字符串表示方法,即实现了 Error() 方法的任何类型都可以被当作 error 使用。

errors.New 是 Go 标准库中的一个函数,它的作用是创建并返回一个实现了 error 接口的值。下面是 errors.New 的源码实现:

package errorsimport "strconv"// 定义错误类型
type errorString struct {s string
}// 实现 error 接口的方法
func (e *errorString) Error() string {return e.s
}// 创建并返回一个 error
func New(text string) error {return &errorString{s: text}
}

工作原理:

  • errorString:是一个结构体,用来存储错误信息(s 字段)。
  • errorString:实现了 Error() 方法,返回错误信息 s。
  • errors.New:函数返回的是 errorString 的指针,因为 *errorString 实现了 error 接口,因此可以作为 error 类型返回。

示例解析

package mainimport ("errors""fmt"
)func main() {err := errors.New("this is an error")fmt.Println(err)         // 输出:this is an errorfmt.Println(err.Error()) // 输出:this is an error
}
  • errors.New("this is an error"):创建了一个 *errorString 实例,赋值给 err。
  • fmt.Println(err):隐式调用了 Error() 方法,输出 this is an error。
  • err.Error():是显式调用 Error() 方法,输出同样的内容

3.3 显式返回错误

Go 中,错误通常作为函数的返回值返回,开发者需要显式检查并处理。

package mainimport ("errors""fmt"
)func divide(a, b int) (int, error) {if b == 0 {return 0, errors.New("division by zero")}return a / b, nil
}func main() {result, err := divide(10, 0)if err != nil {fmt.Println("Error:", err)} else {fmt.Println("Result:", result)}
}
输出:
Error: division by zero

3.4 自定义错误

3.4.1 自定义

通过定义自定义类型,可以扩展 error 接口。

package mainimport ("fmt"
)type DivideError struct {Dividend intDivisor  int
}func (e *DivideError) Error() string {return fmt.Sprintf("cannot divide %d by %d", e.Dividend, e.Divisor)
}func divide(a, b int) (int, error) {if b == 0 {return 0, &DivideError{Dividend: a, Divisor: b}}return a / b, nil
}func main() {_, err := divide(10, 0)if err != nil {fmt.Println(err) // 输出:cannot divide 10 by 0}
}

3.4.2 使用 errors.Is 和 errors.As

从 Go 1.13 开始,errors 包引入了 errors.Iserrors.As 用于处理错误链:

errors.Is:检查某个错误是否是特定错误或由该错误包装而成。

package mainimport ("errors""fmt"
)var ErrNotFound = errors.New("not found")func findItem(id int) error {return fmt.Errorf("database error: %w", ErrNotFound)
}func main() {err := findItem(1)if errors.Is(err, ErrNotFound) {fmt.Println("Item not found")} else {fmt.Println("Other error:", err)}
}

errors.As:将错误转换为特定类型以便进一步处理。

package mainimport ("errors""fmt"
)type MyError struct {Code intMsg  string
}func (e *MyError) Error() string {return fmt.Sprintf("Code: %d, Msg: %s", e.Code, e.Msg)
}func getError() error {return &MyError{Code: 404, Msg: "Not Found"}
}func main() {err := getError()var myErr *MyErrorif errors.As(err, &myErr) {fmt.Printf("Custom error - Code: %d, Msg: %s\n", myErr.Code, myErr.Msg)}
}

3.5 panic 和 recover

3.5.1 简介

Go 的 panic 用于处理不可恢复的错误,recover 用于从 panic 中恢复。

  • panic
    导致程序崩溃并输出堆栈信息。
    常用于程序无法继续运行的情况。
  • recover:捕获 panic,避免程序崩溃。
    只有在同一个协程中调用 recover 才管用。recover 不能恢复一个不同协程的 panic

引发panic有两种情况,一是程序主动调用,二是程序产生运行时错误,由运行时检测并退出。
发生panic后,程序会从调用panic的函数位置或发生panic的地方立即返回,逐层向上执行函数的defer语句,然后逐层打印函数调用堆栈,直到被recover捕获或运行到最外层函数。即:立即按照逆序执行 defer,并逐级往外层函数栈扩散;defer 就类似 finally
panic不但可以在函数正常流程中抛出,在defer逻辑里也可以再次调用panic或抛出panicdefer里面的panic能够被后续执行的defer捕获。
recover用来捕获panic,阻止panic继续向上传递。recover()defer一起使用,但是defer只有在后面的函数体内直接被掉用才能捕获panic来终止异常,否则返回nil,异常继续向外传递
利用 recover 捕获 panic 时,defer 需要再 panic 之前声明,否则由于 panic 之后的代码得不到执行,因此也无法 recover

3.5.2 示例

简单示例:

package mainimport "fmt"func safeFunction() {defer func() {if r := recover(); r != nil {fmt.Println("Recovered from panic:", r)}}()panic("something went wrong")
}func main() {fmt.Println("Starting program...")safeFunction()fmt.Println("Program continued after panic")
}结果为:
Starting program...
Recovered from panic: something went wrong
Program continued after panic

包含defer 的示例


package mainimport (
"fmt"
)func main() {fmt.Println("外层开始")defer func() {fmt.Println("外层准备recover")if err := recover(); err != nil {fmt.Printf("%#v-%#v\n", "外层", err) // err已经在上一级的函数中捕获了,这里没有异常,只是例行先执行defer,然后执行后面的代码} else {fmt.Println("外层没做啥事")}fmt.Println("外层完成recover")}()fmt.Println("外层即将异常")f()fmt.Println("外层异常后")defer func() {fmt.Println("外层异常后defer")}()
}func f() {fmt.Println("内层开始")defer func() {fmt.Println("内层recover前的defer")}()defer func() {fmt.Println("内层准备recover")if err := recover(); err != nil {fmt.Printf("%#v-%#v\n", "内层", err) // 这里err就是panic传入的内容}fmt.Println("内层完成recover")}()defer func() {fmt.Println("内层异常前recover后的defer")}()panic("异常信息")defer func() {fmt.Println("内层异常后的defer")}()fmt.Println("内层异常后语句") //recover捕获的一级或者完全不捕获这里开始下面代码不会再执行
}结果:
外层开始
外层即将异常
内层开始
内层异常前recover后的defer
内层准备recover
"内层"-"异常信息"
内层完成recover
内层recover前的defer
外层异常后
外层异常后defer
外层准备recover
外层没做啥事
外层完成recover

3.5.3 recover 恢复后获得堆栈跟踪

当我们恢复 panic 时,我们就释放了它的堆栈跟踪。实际上,在上述程序里,恢复 panic 之后,我们就失去了堆栈跟踪。
有一种办法可以打印出堆栈跟踪,就是使用 Debug 包中的 PrintStack 函数。

package mainimport (  "fmt""runtime/debug"
)func r() {          if r := recover(); r != nil {        fmt.Println("Recovered", r)debug.PrintStack()}
}func a() {          defer r()n := []int{ 5, 7, 4}fmt.Println(n[3])fmt.Println("normally returned from a")
}func main() {          a()fmt.Println("normally returned from main")
}

在上面的程序中,我们使用了 debug.PrintStack() 打印堆栈跟踪。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词