Golang 语言怎么避免空指针引发的 panic

简介: Golang 语言怎么避免空指针引发的 panic

介绍

在 Golang 语言项目开发中,变量操作不当就会触发空指针引发程序 panic。空指针就是未分配内存的指针类型的变量,变量的值是 nil,因为操作空指针会引发 panic,所以我们在程序开发中要特别小心。

结构体指针类型返回值

在调用结构体指针类型返回值的函数或方法时,并且需要操作返回值的字段或方法,此时,我们就需要注意触发空指针引发的 panic。

操作返回值的字段:

func main() {
 user := GetUser()
 fmt.Println(user)
 fmt.Println(user.Id)
}
func GetUser() (user *User) {
 return
}
type User struct {
 Id   int
 Name string
}

阅读上面这段代码,我们通过调用函数 GetUser() 获取 *User 类型的返回值,因为返回值变量是空指针,当我们访问返回值的字段时,程序引发 panic。

避免此类空指针问题,一是可以在返回值包含指针类型变量的函数或方法中,在函数体开头初始化返回值的指针类型变量;二是在调用结构体指针类型返回值的函数或方法时,在操作返回值的字段或方法时,先判定返回值是否为 nil(空指针)。

func main() {
 user := GetUser()
 fmt.Println(user)
 if user != nil {
  fmt.Println(user.Id)
 }
}
func GetUser() (user *User) {
 user = new(User)
 // user = &User{}
 return
}
type User struct {
 Id   int
 Name string
}

操作返回值的方法:

func main() {
 user := GetUser()
 user.Login()
}
func GetUser() (user *User) {
 return
}
type User struct {
 Id   int
 Name string
}
func (u User) Login() {
}

阅读上面这段代码,我们通过调用函数 GetUser() 获取 *User 类型的返回值,因为返回值变量是空指针,当我们访问返回值的方法 Login() 时,程序触发空指针引发 panic。

避免此类空指针问题,一是可以在返回值是指针类型变量的函数或方法的函数体中,开头先初始化返回值的指针类型变量;二是类型方法的接收者使用指针类型。

func main() {
 user := GetUser()
 user.Login()
}
func GetUser() (user *User) {
 user = new(User)
 // user = &User{}
 return
}
type User struct {
 Id   int
 Name string
}
func (u *User) Login() {
}

03

结构体指针类型 value 的 Map

在 Golang 语言程序开发中,经常会操作结构体指针类型 value 的 Map,也需要注意触发空指针引发 panic。

func main() {
 var userData map[int]*User
 fmt.Println(userData[1].Name)
}
type User struct {
 Id   int
 Name string
}

阅读上面这段代码,我们定义 map 类型的变量 userData,key 是 int 类型,value 是结构体指针类型,我们访问 map 的值时,因为值是空指针,所以会引发 panic。

避免此类空指针问题,我们可以使用 ok-idiom 模式判断键值是否存在,如果键值存在(判断键值是否为 nil),我们访问键值的字段,否则不访问。通过这种方式,也可以避免触发空指针引发 panic。

func main() {
 var userData map[int]*User
 if val, ok := userData[1]; ok {
  fmt.Println(val.Name)
 }
}
type User struct {
 Id   int
 Name string
}

04

defer 延迟调用

关键字 defer 延迟调用函数,虽然被调用函数会延迟调用,但是被调用函数的变量会先被注册。所以,如果被调用函数的变量是空指针,就会引发 panic。

func main() {
 res, err := http.Get("http://www.baidu2022.com/robots.txt") // 伪造错误请求
 defer res.Body.Close()
 if err != nil {
  log.Fatal(err)
 }
 body, err := io.ReadAll(res.Body)
 if err != nil {
  log.Fatal(err)
 }
 fmt.Printf("%s", body)
}

阅读上面这段代码,使用 defer 延迟调用函数释放资源,因为我们将 defer 放在错误检查之后,所以如果返回值 res 是空指针,就会引发 panic。

避免此类空指针问题,我们可以在使用 defer 调用之前,先做错误检查,并且遇到错误后停止向下执行。

05

总结

本文我们介绍一些 Golang 语言开发需要避免空指针引发 panic 的场景,虽然都比较简单,但是新手很容易踩“坑”。欢迎读者朋友们在评论区与大家分享更多因为触发空指针引发 panic 的场景。

推荐阅读:


目录
相关文章
|
1月前
|
Java 容器
双指针(JAVA语言)
双指针(JAVA语言)
双指针(JAVA语言)
|
1月前
|
存储 Java C#
C++语言模板类对原生指针的封装与模拟
C++|智能指针的智能性和指针性:模板类对原生指针的封装与模拟
|
1月前
|
算法 Java 程序员
面向对象编程(OOP)通过对象组合构建软件,C语言虽是过程式语言,但可通过结构体、函数指针模拟OOP特性
【6月更文挑战第15天】面向对象编程(OOP)通过对象组合构建软件,C语言虽是过程式语言,但可通过结构体、函数指针模拟OOP特性。封装可使用结构体封装数据和方法,如模拟矩形对象。继承则通过结构体嵌套实现静态继承。多态可通过函数指针模拟,但C不支持虚函数表,实现复杂。C语言能体现OOP思想,但不如C++、Java等语言原生支持。
33 7
|
1月前
|
JSON Go 数据格式
Go 语言基础之指针、复合类型【数组、切片、指针、map、struct】(4)
Go 语言基础之指针、复合类型【数组、切片、指针、map、struct】
|
1月前
|
Java 编译器 Go
Go 语言基础之指针、复合类型【数组、切片、指针、map、struct】(3)
Go 语言基础之指针、复合类型【数组、切片、指针、map、struct】
|
1月前
|
存储 安全 Go
Go 语言基础之指针、复合类型【数组、切片、指针、map、struct】(2)
Go 语言基础之指针、复合类型【数组、切片、指针、map、struct】
|
1月前
|
Java Go 索引
Go 语言基础之指针、复合类型【数组、切片、指针、map、struct】(1)
Go 语言基础之指针、复合类型【数组、切片、指针、map、struct】
|
1月前
|
Go
如何理解Go语言中的值接收者和指针接收者?
Go语言中,函数和方法可使用值或指针接收者。值接收者是参数副本,内部修改不影响原值,如示例中`ChangeValue`无法改变`MyStruct`的`Value`。指针接收者则允许修改原值,因为传递的是内存地址。选择接收者类型应基于是否需要修改参数,值接收者用于防止修改,指针接收者用于允许修改。理解这一区别对编写高效Go代码至关重要。
|
2月前
|
Java Go
一文带你速通go语言指针
Go语言指针入门指南:简述指针用于提升效率,通过地址操作变量。文章作者sharkChili是Java/CSDN专家,维护Java Guide项目。文中介绍指针声明、取值,展示如何通过指针修改变量值及在函数中的应用。通过实例解析如何使用指针优化函数,以实现对原变量的直接修改。作者还邀请读者加入交流群深入探讨,并鼓励关注其公众号“写代码的SharkChili”。
32 0
|
2月前
|
存储 Go
Go 语言指针
Go 语言指针
19 0