Go 语言接口
接口(interface)是 Go 语言中的一种类型,用于定义行为的集合,它通过描述类型必须实现的方法,规定了类型的行为契约。
Go 语言提供了另外一种数据类型即接口,它把所有的具有共性的方法定义在一起,任何其他类型只要实现了这些方法就是实现了这个接口。
Go 的接口设计简单却功能强大,是实现多态和解耦的重要工具。
接口可以让我们将不同的类型绑定到一组公共的方法上,从而实现多态和灵活的设计。
接口的特点
隐式实现:
- Go 中没有关键字显式声明某个类型实现了某个接口。
- 只要一个类型实现了接口要求的所有方法,该类型就自动被认为实现了该接口。
接口类型变量:
- 接口变量可以存储实现该接口的任意值。
- 接口变量实际上包含了两个部分:
- 动态类型:存储实际的值类型。
- 动态值:存储具体的值。
零值接口:
- 接口的零值是
nil。 - 一个未初始化的接口变量其值为
nil,且不包含任何动态类型或值。
空接口:
- 定义为
interface{},可以表示任何类型。
接口的常见用法
- 多态:不同类型实现同一接口,实现多态行为。
- 解耦:通过接口定义依赖关系,降低模块之间的耦合。
- 泛化 :使用空接口
interface{}表示任意类型。
接口定义和实现
接口定义使用关键字 interface,其中包含方法声明。
实例
/* 定义接口 */
type interface_name interface {
method_name1 return_type
method_name2 return_type
method_name3 return_type
...
method_namen return_type
}
/* 定义结构体 */
type struct_name struct {
/* variables */
}
/* 实现接口方法 */
func (struct_name_variable struct_name) method_name1() return_type {
/* 方法实现 */
}
...
func (struct_name_variable struct_name) method_namen() return_type {
/* 方法实现*/
}
定义一个简单接口:
type Shape interface {
Area() float64
Perimeter() float64
}
Shape是一个接口,定义了两个方法:Area和Perimeter。- 任意类型只要实现了这两个方法,就被认为实现了
Shape接口。
实现接口: 类型通过实现接口要求的所有方法来实现接口。
实例
package main
import (
"fmt"
"math"
)
// 定义接口
type Shape interface {
Area() float64
Perimeter() float64
}
// 定义一个结构体
type Circle struct {
Radius float64
}
// Circle 实现 Shape 接口
func (c Circle) Area() float64 {
return math.Pi * c.Radius * c.Radius
}
func (c Circle) Perimeter() float64 {
return 2 * math.Pi * c.Radius
}
func main() {
c := Circle{Radius: 5}
var s Shape = c // 接口变量可以存储实现了接口的类型
fmt.Println("Area:", s.Area())
fmt.Println("Perimeter:", s.Perimeter())
}
执行以上代码,输出结果为:
Area: 78.53981633974483
Perimeter: 31.41592653589793
空接口
空接口 interface{} 是 Go 的特殊接口,表示所有类型的超集。
- 任意类型都实现了空接口。
- 常用于需要存储任意类型数据的场景,如泛型容器、通用参数等。
实例
package main
import "fmt"
func printValue(val interface{}) {
fmt.Printf("Value: %v, Type: %T\n", val, val)
}
func main() {
printValue(42) // int
printValue("hello") // string
printValue(3.14) // float64
printValue(\[\]int{1, 2}) // slice
}
执行以上代码,输出结果为:
Value: 42, Type: int
Value: hello, Type: string
Value: 3.14, Type: float64
Value: [1 2], Type: []int
类型断言
类型断言用于从接口类型中提取其底层值。
基本语法:
value := iface.(Type)
iface是接口变量。Type是要断言的具体类型。- 如果类型不匹配,会触发
panic。
实例
package main
import "fmt"
func main() {
var i interface{} = "hello"
str := i.(string) // 类型断言
fmt.Println(str) // 输出:hello
}
带检查的类型断言
为了避免 panic,可以使用带检查的类型断言:
value, ok := iface.(Type)
ok是一个布尔值,表示断言是否成功。- 如果断言失败,
value为零值,ok为false。
实例
package main
import "fmt"
func main() {
var i interface{} = 42
if str, ok := i.(string); ok {
fmt.Println("String:", str)
} else {
fmt.Println("Not a string")
}
}
执行以上代码,输出结果为:
Not a string
类型选择(type switch)
type switch 是 Go 中的语法结构,用于根据接口变量的具体类型执行不同的逻辑。
实例
package main
import "fmt"
func printType(val interface{}) {
switch v := val.(type) {
case int:
fmt.Println("Integer:", v)
case string:
fmt.Println("String:", v)
case float64:
fmt.Println("Float:", v)
default:
fmt.Println("Unknown type")
}
}
func main() {
printType(42)
printType("hello")
printType(3.14)
printType(\[\]int{1, 2, 3})
}
执行以上代码,输出结果为:
Integer: 42
String: hello
Float: 3.14
Unknown type
接口组合
接口可以通过嵌套组合,实现更复杂的行为描述。
实例
package main
import "fmt"
type Reader interface {
Read() string
}
type Writer interface {
Write(data string)
}
type ReadWriter interface {
Reader
Writer
}
type File struct{}
func (f File) Read() string {
return "Reading data"
}
func (f File) Write(data string) {
fmt.Println("Writing data:", data)
}
func main() {
var rw ReadWriter = File{}
fmt.Println(rw.Read())
rw.Write("Hello, Go!")
}
动态值和动态类型
接口变量实际上包含了两部分:
- 动态类型:接口变量存储的具体类型。
- 动态值:具体类型的值。
动态值和动态类型示例:
实例
package main
import "fmt"
func main() {
var i interface{} = 42
fmt.Printf("Dynamic type: %T, Dynamic value: %v\n", i, i)
}
执行以上代码,输出结果为:
Dynamic type: int, Dynamic value: 42
接口的零值
接口的零值是 nil。
当接口变量的动态类型和动态值都为 nil 时,接口变量为 nil。
接口零值示例:
实例
package main
import "fmt"
func main() {
var i interface{}
fmt.Println(i == nil) // 输出:true
}
练习实例
以下两个实例演示了接口的使用:
实例 1
package main
import (
"fmt"
)
type Phone interface {
call()
}
type NokiaPhone struct {
}
func (nokiaPhone NokiaPhone) call() {
fmt.Println("I am Nokia, I can call you!")
}
type IPhone struct {
}
func (iPhone IPhone) call() {
fmt.Println("I am iPhone, I can call you!")
}
func main() {
var phone Phone
phone = new(NokiaPhone)
phone.call()
phone = new(IPhone)
phone.call()
}
在上面的例子中,我们定义了一个接口 Phone ,接口里面有一个方法 call()。然后我们在 main 函数里面定义了一个 Phone 类型变量,并分别为之赋值为 NokiaPhone 和 IPhone。然后调用 call() 方法,输出结果如下:
I am Nokia, I can call you!
I am iPhone, I can call you!
第二个接口实例:
实例
package main
import "fmt"
type Shape interface {
area() float64
}
type Rectangle struct {
width float64
height float64
}
func (r Rectangle) area() float64 {
return r.width * r.height
}
type Circle struct {
radius float64
}
func (c Circle) area() float64 {
return 3.14 * c.radius * c.radius
}
func main() {
var s Shape
s = Rectangle{width: 10, height: 5}
fmt.Printf("矩形面积: %f\n", s.area())
s = Circle{radius: 3}
fmt.Printf("圆形面积: %f\n", s.area())
}
以上实例中,我们定义了一个 Shape 接口,它定义了一个方法 area(),该方法返回一个 float64 类型的面积值。然后,我们定义了两个结构体 Rectangle 和 Circle,它们分别实现了 Shape 接口的 area() 方法。在 main() 函数中,我们首先定义了一个 Shape 类型的变量 s,然后分别将 Rectangle 和 Circle 类型的实例赋值给它,并通过 area() 方法计算它们的面积并打印出来,输出结果如下:
矩形面积: 50.000000
圆形面积: 28.260000
需要注意的是,接口类型变量可以存储任何实现了该接口的类型的值。在示例中,我们将 Rectangle 和 Circle 类型的实例都赋值给了 Shape 类型的变量 s,并通过 area() 方法调用它们的面积计算方法。
Go 语言泛型
泛型是 Go 语言在 1.18 版本中引入的重要特性,它让开发者能够编写更加灵活和可重用的代码。
泛型主要通过以下两个核心概念来实现:
- **类型参数(Type Parameters):**允许你在函数或类型定义中使用一个或多个类型作为参数。
- **类型约束(Type Constraints):**指定类型参数必须满足的条件,确保在函数内部可以安全地操作这些类型。
| 概念 | 作用 | 示例 |
|---|---|---|
| 类型参数 | 在函数或类型名后声明,表示待定的类型。 | [T any] |
| 类型约束 | 定义类型参数必须满足的条件(如支持的操作符或方法)。 | int,float64,comparable,constraints.Ordered,any |
any |
约束类型参数为任何类型。 | [T any] |
comparable |
约束类型参数为可比较的类型。 | [K comparable] |
泛型(Generics)允许我们编写不依赖特定数据类型的代码。
在引入泛型之前,如果我们想要处理不同类型的数据,通常需要为每种类型编写重复的函数。
传统方式的局限性:
实例
// 处理 int 类型的函数
func MaxInt(a, b int) int {
if a > b {
return a
}
return b
}
// 处理 float64 类型的函数
func MaxFloat(a, b float64) float64 {
if a > b {
return a
}
return b
}
使用泛型的解决方案:
实例
// 一个函数处理多种类型
func MaxT comparable(a, b T) T {
if a > b {
return a
}
return b
}
泛型语法详解
类型参数声明
泛型函数和类型通过类型参数列表来声明,语法为 [类型参数 约束]。
实例
// 基本语法结构
func 函数名T 约束(参数 T) 返回值类型 {
// 函数体
}
type 类型名T 约束 struct {
// 结构体字段
}
类型参数命名约定
- 通常使用大写字母:
T、K、V、E等 T:表示 Type(类型)K:表示 Key(键)V:表示 Value(值)E:表示 Element(元素)
约束(Constraints)
约束定义了类型参数必须满足的条件,是泛型的核心概念。
内置约束
1. any 约束
any 是空接口 interface{} 的别名,表示任何类型都可以。
实例
func PrintAnyT any(value T) {
fmt.Printf("Value: %v, Type: %T\n", value, value)
}
// 使用示例
PrintAny(42) // Value: 42, Type: int
PrintAny("hello") // Value: hello, Type: string
PrintAny(3.14) // Value: 3.14, Type: float64
2. comparable 约束
comparable 表示类型支持 == 和 != 操作符。
实例
func FindIndexT comparable(slice \[\]T, target T) int {
for i, v := range slice {
if v == target {
return i
}
}
return -1
}
// 使用示例
numbers := \[\]int{1, 2, 3, 4, 5}
fmt.Println(FindIndex(numbers, 3)) // 输出: 2
names := \[\]string{"Alice", "Bob", "Charlie"}
fmt.Println(FindIndex(names, "Bob")) // 输出: 1
3. 联合约束(Union Constraints)
使用 | 运算符组合多个类型。
实例
// 数字类型约束
type Number interface {
int | int8 | int16 | int32 | int64 |
uint | uint8 | uint16 | uint32 | uint64 |
float32 | float64
}
func AddT Number(a, b T) T {
return a + b
}
// 使用示例
fmt.Println(Add(10, 20)) // 输出: 30
fmt.Println(Add(3.14, 2.71)) // 输出: 5.85
自定义约束
1. 方法约束
定义需要特定方法的约束。
实例
// 定义 Stringer 约束
type Stringer interface {
String() string
}
func PrintStringT Stringer(value T) {
fmt.Println(value.String())
}
// 实现自定义类型
type Person struct {
Name string
Age int
}
func (p Person) String() string {
return fmt.Sprintf("%s (%d years old)", p.Name, p.Age)
}
// 使用示例
person := Person{Name: "Alice", Age: 25}
PrintString(person) // 输出: Alice (25 years old)
2. 复杂约束
结合类型和方法要求。
实例
// 要求类型是数字且实现 String() 方法
type NumericStringer interface {
Number
String() string
}
泛型函数实践
1. 通用工具函数
实例
// 交换两个值
func SwapT any(a, b T) (T, T) {
return b, a
}
// 判断切片是否包含元素
func ContainsT comparable(slice \[\]T, target T) bool {
for _, item := range slice {
if item == target {
return true
}
}
return false
}
// 去重函数
func UniqueT comparable(slice \[\]T) \[\]T {
seen := make(mapTbool)
result := \[\]T{}
for _, item := range slice {
if !seenitem {
seenitem = true
result = append(result, item)
}
}
return result
}
// 使用示例
func main() {
// Swap 示例
a, b := 10, 20
a, b = Swap(a, b)
fmt.Printf("a=%d, b=%d\n", a, b) // 输出: a=20, b=10
// Contains 示例
numbers := \[\]int{1, 2, 3, 4, 5}
fmt.Println(Contains(numbers, 3)) // 输出: true
// Unique 示例
duplicates := \[\]int{1, 2, 2, 3, 4, 4, 5}
unique := Unique(duplicates)
fmt.Println(unique) // 输出: 1 2 3 4 5
}
2. 数学运算函数
实例
// 求切片最大值
func MaxT Number(slice \[\]T) T {
if len(slice) == 0 {
var zero T
return zero
}
max := slice0
for _, value := range slice1: {
if value > max {
max = value
}
}
return max
}
// 求切片最小值
func MinT Number(slice \[\]T) T {
if len(slice) == 0 {
var zero T
return zero
}
min := slice0
for _, value := range slice1: {
if value < min {
min = value
}
}
return min
}
// 求切片平均值
func AverageT Number(slice \[\]T) float64 {
if len(slice) == 0 {
return 0
}
var sum T
for _, value := range slice {
sum += value
}
return float64(sum) / float64(len(slice))
}
// 使用示例
func main() {
ints := \[\]int{1, 5, 3, 9, 2}
floats := \[\]float64{1.1, 5.5, 3.3, 9.9, 2.2}
fmt.Printf("Max int: %d\n", Max(ints)) // 输出: 9
fmt.Printf("Min float: %.1f\n", Min(floats)) // 输出: 1.1
fmt.Printf("Average: %.2f\n", Average(floats)) // 输出: 4.40
}
泛型类型
1. 泛型结构体
实例
// 泛型栈实现
type StackT any struct {
elements \[\]T
}
// 入栈
func (s *StackT) Push(value T) {
s.elements = append(s.elements, value)
}
// 出栈
func (s *StackT) Pop() (T, bool) {
if len(s.elements) == 0 {
var zero T
return zero, false
}
lastIndex := len(s.elements) - 1
value := s.elementslastIndex
s.elements = s.elements:lastIndex
return value, true
}
// 查看栈顶元素
func (s *StackT) Peek() (T, bool) {
if len(s.elements) == 0 {
var zero T
return zero, false
}
return s.elementslen(s.elements)-1, true
}
// 判断栈是否为空
func (s *StackT) IsEmpty() bool {
return len(s.elements) == 0
}
// 使用示例
func main() {
// 整数栈
intStack := Stackint{}
intStack.Push(1)
intStack.Push(2)
intStack.Push(3)
fmt.Println(intStack.Pop()) // 输出: 3 true
// 字符串栈
stringStack := Stackstring{}
stringStack.Push("hello")
stringStack.Push("world")
fmt.Println(stringStack.Pop()) // 输出: world true
}
2. 泛型映射(Map)
实例
// 线程安全的泛型映射
type SafeMapK comparable, V any struct {
data mapKV
mutex sync.RWMutex
}
// 创建新的 SafeMap
func NewSafeMapK comparable, V any() *SafeMapK, V {
return &SafeMapK, V{
data: make(mapKV),
}
}
// 设置键值对
func (m *SafeMapK, V) Set(key K, value V) {
m.mutex.Lock()
defer m.mutex.Unlock()
m.datakey = value
}
// 获取值
func (m *SafeMapK, V) Get(key K) (V, bool) {
m.mutex.RLock()
defer m.mutex.RUnlock()
value, exists := m.datakey
return value, exists
}
// 删除键
func (m *SafeMapK, V) Delete(key K) {
m.mutex.Lock()
defer m.mutex.Unlock()
delete(m.data, key)
}
// 获取所有键
func (m *SafeMapK, V) Keys() \[\]K {
m.mutex.RLock()
defer m.mutex.RUnlock()
keys := make(\[\]K, 0, len(m.data))
for key := range m.data {
keys = append(keys, key)
}
return keys
}
// 使用示例
func main() {
// 创建字符串到整数的映射
scores := NewSafeMapstring, int()
scores.Set("Alice", 95)
scores.Set("Bob", 87)
if score, exists := scores.Get("Alice"); exists {
fmt.Printf("Alice's score: %d\n", score) // 输出: Alice's score: 95
}
fmt.Println("Keys:", scores.Keys()) // 输出: Keys: Alice Bob
}
类型推断
Go 编译器能够自动推断类型参数,让代码更加简洁。
实例
// 无需显式指定类型
func main() {
// 类型推断示例
fmt.Println(Max(\[\]int{1, 2, 3})) // 编译器推断 T 为 int
fmt.Println(Max(\[\]float64{1.1, 2.2})) // 编译器推断 T 为 float64
// 显式指定类型(有时需要)
var result int = Maxint(\[\]int{1, 2, 3})
fmt.Println(result)
}
实践练习
练习 1:实现泛型过滤器
编写一个 Filter 函数,根据条件过滤切片元素。
实例
// 你的实现代码在这里
func FilterT any(slice \[\]T, predicate func(T) bool) \[\]T {
// 实现过滤逻辑
}
// 测试代码
func main() {
numbers := \[\]int{1, 2, 3, 4, 5, 6}
even := Filter(numbers, func(n int) bool {
return n%2 == 0
})
fmt.Println(even) // 应该输出: 2 4 6
}
练习 2:实现泛型映射函数
编写一个 Map 函数,将切片中的每个元素转换为另一种类型。
实例
// 你的实现代码在这里
func MapT any, U any(slice \[\]T, mapper func(T) U) \[\]U {
// 实现映射逻辑
}
// 测试代码
func main() {
numbers := \[\]int{1, 2, 3, 4, 5}
strings := Map(numbers, func(n int) string {
return fmt.Sprintf("Number: %d", n)
})
fmt.Println(strings)
}
常见问题与注意事项
1. 性能考虑
泛型在编译时进行类型特化,运行时性能与手写特定类型代码相当。
2. 类型约束的选择
- 使用
any时最灵活,但功能受限 - 使用
comparable支持相等比较 - 使用联合约束限制可用的具体类型
3. 错误处理
实例
// 良好的错误处理实践
func SafeMaxT Number(slice \[\]T) (T, error) {
if len(slice) == 0 {
var zero T
return zero, errors.New("slice is empty")
}
return Max(slice), nil
}
Go 错误处理
Go 语言通过内置的错误接口提供了非常简单的错误处理机制。
Go 语言的错误处理采用显式返回错误的方式,而非传统的异常处理机制。这种设计使代码逻辑更清晰,便于开发者在编译时或运行时明确处理错误。
Go 的错误处理主要围绕以下机制展开:
error接口:标准的错误表示。- 显式返回值:通过函数的返回值返回错误。
- 自定义错误:可以通过标准库或自定义的方式创建错误。
panic和recover:处理不可恢复的严重错误。
错误接口
Go 标准库定义了一个 error 接口,表示一个错误的抽象。
error 类型是一个接口类型,这是它的定义:
prettyprint
<span style="color:#2b2b2b"><span style="color:#2b2b2b">type error </span><span style="color:#6b8e5a">interface</span> <span style="color:#171c19">{</span>
<span style="color:#5c7c8a">Error</span><span style="color:#171c19">()</span> <span style="color:#6b8e5a">string</span>
<span style="color:#171c19">}</span></span>
- 实现
error接口 :任何实现了 方法的类型都可以作为错误。Error() Error()方法返回一个描述错误的字符串。
使用 errors 包创建错误
我们可以在编码中通过实现 error 接口类型来生成错误信息。
创建一个简单错误:
实例
package main
import (
"errors"
"fmt"
)
func main() {
err := errors.新("这是个错误")
fmt。Println(err) 输出:this is a error
}
函数通常在最后的返回值中返回错误信息,使用 errors.New 可返回一个错误信息:
prettyprint
<span style="color:#2b2b2b"><span style="color:#2b2b2b">func </span><span style="color:#5c7c8a">Sqrt</span><span style="color:#171c19">(</span><span style="color:#2b2b2b">f float64</span><span style="color:#171c19">)</span> <span style="color:#171c19">(</span><span style="color:#2b2b2b">float64</span><span style="color:#171c19">,</span><span style="color:#2b2b2b"> error</span><span style="color:#171c19">)</span> <span style="color:#171c19">{</span>
<span style="color:#6b8e5a">if</span><span style="color:#2b2b2b"> f </span><span style="color:#171c19"><</span> <span style="color:#8c7aa9">0</span> <span style="color:#171c19">{</span>
<span style="color:#6b8e5a">return</span> <span style="color:#8c7aa9">0</span><span style="color:#171c19">,</span><span style="color:#2b2b2b"> errors</span><span style="color:#171c19">.</span><span style="color:#5c7c8a">New</span><span style="color:#171c19">(</span><span style="color:#b26a5e">"math: square root of negative number"</span><span style="color:#171c19">)</span>
<span style="color:#171c19">}</span>
<span style="color:#9aa6ac"><em>// 实现</em></span>
<span style="color:#171c19">}</span></span>
在下面的例子中,我们在调用 Sqrt 的时候传递的一个负数,然后就得到了 non-nil 的 error 对象,将此对象与 nil 比较,结果为 true,所以 fmt.Println(fmt 包在处理 error 时会调用 Error 方法)被调用,以输出错误,请看下面调用的示例代码:
prettyprint
<span style="color:#2b2b2b"><span style="color:#2b2b2b">result</span><span style="color:#171c19">,</span><span style="color:#2b2b2b"> err</span><span style="color:#171c19">:=</span> <span style="color:#5c7c8a">Sqrt</span><span style="color:#171c19">(-</span><span style="color:#8c7aa9">1</span><span style="color:#171c19">)</span>
<span style="color:#6b8e5a">if</span><span style="color:#2b2b2b"> err </span><span style="color:#171c19">!=</span> <span style="color:#6b8e5a">nil</span> <span style="color:#171c19">{</span><span style="color:#2b2b2b">
fmt</span><span style="color:#171c19">.</span><span style="color:#5c7c8a">Println</span><span style="color:#171c19">(</span><span style="color:#2b2b2b">err</span><span style="color:#171c19">)</span>
<span style="color:#171c19">}</span></span>
显式返回错误
Go 中,错误通常作为函数的返回值返回,开发者需要显式检查并处理。
显式返回错误:
实例
package main
import (
"errors"
"fmt"
)
func divide(a, b int) (int, error) {
if b == 0 {
return 0,错误。New("除以零")}
返回 a / b, nil
}
函数 main() {
结果,err := 除(10, 0)
如果 err != 零 {
fmt.Println("Error:", err)
} 否则 {
fmt.Println("结果:",结果)
}
}
输出:
prettyprint
<span style="color:#2b2b2b"><span style="color:#5c7c8a">Error</span><span style="color:#171c19">:</span><span style="color:#2b2b2b"> division </span><span style="color:#6b8e5a">by</span><span style="color:#2b2b2b"> zero</span></span>
自定义错误
通过定义自定义类型,可以扩展 error 接口。
自定义错误类型:
实例
package main
import (
"fmt"
)
类型 DivideError struct {
Dividend int
Divisor int
}
func (e *DivideError) Error() 字符串 {
返回 fmt.Sprintf("不能将%d除以%d"),例如。股息,e。除子)
}
函数 除子(a, b int) (整数, 错误) {
如果 b == 0 {
返回 0, &DivideError{Dividend: a, 除子:b}
}
返回 a / b, nil
}
func main() {
_, err := divide(10, 0)
如果 err != nil {
fmt。Println(err) 输出:无法除以10
}
}
FMT 包与错误格式化
fmt包提供了对错误的格式化输出支持:
%v:默认格式。%+v:如果支持,显示详细的错误信息。%s:作为字符串输出。
实例
package main
import (
"fmt"
)
定义一个 DivideError 结构
类型 DivideError struct {
dividee int
divider int
}
实现 'error' 接口
func (de*DivideError) Error() 字符串 {
strFormat := '
无法继续,分频器为零。
除法: %d
分频器: 0
'
返回 FMT。Sprintf(strFormat, de.dividee)
}
定义 'int' 类型除法运算的函数
func Divide(varDividee int, varDivider int) (result int, errorMsg string) {
if varDivider == 0 {
dData := DivideError{
dividee: varDividee,
divider: varDivider,
}
errorMsg = dData.Error()
return
} 否则 {
return varDividee / varDivider, ""
}
} func main() {
正常情况
如果结果,errorMsg := 除法(100, 10); errorMsg == "" {
fmt.Println("100/10 = ", 结果)
}
当除数为零的时候会返回错误信息
if _, errorMsg := Divide(100, 0);errorMsg != "" {
fmt.Println("errorMsg 是: ", errorMsg)
}
}
执行以上程序,输出结果为:
prettyprint
<span style="color:#2b2b2b"><span style="color:#8c7aa9">100</span><span style="color:#171c19">/</span><span style="color:#8c7aa9">10</span> <span style="color:#171c19">=</span> <span style="color:#8c7aa9">10</span><span style="color:#2b2b2b">
errorMsg </span><span style="color:#6b8e5a">is</span><span style="color:#171c19">:</span>
<span style="color:#5c7c8a">Cannot</span><span style="color:#2b2b2b"> proceed</span><span style="color:#171c19">,</span><span style="color:#2b2b2b"> the divider </span><span style="color:#6b8e5a">is</span><span style="color:#2b2b2b"> zero</span><span style="color:#171c19">.</span><span style="color:#2b2b2b">
dividee</span><span style="color:#171c19">:</span> <span style="color:#8c7aa9">100</span><span style="color:#2b2b2b">
divider</span><span style="color:#171c19">:</span> <span style="color:#8c7aa9">0</span>
</span>
使用错误。是和错误。作为
从 Go 1.13 开始, 包引入了 和 用于处理错误链:errors``errors.Is``errors.As
errors.Is
检查某个错误是否是特定错误或由该错误包装而成。
实例
package main
import (
"errors"
"fmt"
)
var ErrNotFound = errors.New("not found")
func findItem(id int) 错误 {
return fmt.Errorf("database error: %w", ErrNotFound)
}
func main() {
err := findItem(1)
如果有错误。Is(err, ErrNotFound) {
fmt.Println("未找到物品")
} 否则 {
fmt.Println("其他错误:",呃)
}
}
错误。作为
将错误转换为特定类型以便进一步处理。
实例
package main
import (
"errors"
"fmt"
)
类型 MyError struct {
Code int
Msg string
}
func (e *MyError) Error() 字符串 {
返回 fmt.Sprintf("代码: %d, 消息:%s", e.代码,e。Msg)
}
func getError() error {
return &MyError{Code: 404, Msg: "Not Found"}
}
func main() {
err := getError()
var myErr *MyError
(如果有错误)。As(err, &myErr) {
fmt.Printf("自定义错误 - 代码:%d,消息:%s**\n**",myErr.代码,我的呃。消息)
}
}
Panic 和 恢复
Go 的 panic 用于处理不可恢复的错误,recover 用于从 panic 中恢复。
恐慌:
- 导致程序崩溃并输出堆栈信息。
- 常用于程序无法继续运行的情况。
恢复:
- 捕获 ,避免程序崩溃。
panic
实例
package main
import "fmt"
func safeFunction() {
defer func() {
if r := recover(); r != nil {
fmt.Println("从恐慌中恢复过来:", r)
}
}()
panic("出了问题发生
了")} func main() {
fmt.Println("启动程序...")
safeFunction()
fmt.Println("恐慌后继续计划")
}
执行以上代码,输出结果为:
prettyprint
<span style="color:#2b2b2b"><span style="color:#5c7c8a">Starting</span><span style="color:#2b2b2b"> program</span><span style="color:#171c19">...</span>
<span style="color:#5c7c8a">Recovered</span> <span style="color:#6b8e5a">from</span><span style="color:#2b2b2b"> panic</span><span style="color:#171c19">:</span><span style="color:#2b2b2b"> something went wrong
</span><span style="color:#5c7c8a">Program</span><span style="color:#2b2b2b"> continued after panic</span></span>