Golang Go语言中有关 nil 的一个疑问?
今天突然意识到一个问题:go 的代码中,关于调用结构体指针绑定的方法的时候,方法里面似乎都没有判断该指针是否为 nil 的情况。
type A struct {
a int
}
func (a *A) Run() {
fmt.Print(a.a)
}
func TestA(t *testing.T) {
var a *A
a.Run()
}
以上代码会报错,原因也很明显,a 为 nil, a.Run() 方法中,访问 a.a 报错。 看了一下自己之前写的所有 go 代码,所有 结构体指针绑定的方法里面,都没有判断是否为 nil 的情况。
Golang Go语言中有关 nil 的一个疑问?
更多关于Golang Go语言中有关 nil 的一个疑问?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
会 go 不会 go 的都哑口无言了
更多关于Golang Go语言中有关 nil 的一个疑问?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
在一些项目源码里见到过判断的,不过我是觉得没必要。
会报 panic
if a!=nil ?
这种一般是调用者保证它非 nil
这取决于哪里需要关心 a 为 nil 这件事
golang<br>type A struct {<br> a int<br>}<br><br>func (a *A) Run() {<br> fmt.Print(a.a)<br>}<br><br>func NewA()*A{<br> return &A{}<br>}<br><br>func TestA(t *testing.T) {<br> //var a *A // 这一步只是声明了一个变量 a 为 结构体 A 指针类型,没有实例化<br> //<a target="_blank" href="http://a.Run" rel="nofollow noopener">a.Run</a>()<br> a := NewA() // 一般都是这样去实例化 <br> <a target="_blank" href="http://a.Run" rel="nofollow noopener">a.Run</a>()<br>}<br>
你这代码只是声明了 变量 a 是 A 类型。没有实例化
判不判断 nil 看需求,有时候结构体绑定的方法是当函数来用的golang<br><br>import "fmt"<br><br>type A struct {<br> a int<br>}<br><br>func (a *A) Run() {<br> fmt.Print(a.a)<br>}<br><br>func main() {<br> var fn = (*A).Run<br> fmt.Println(fn)<br> fn(&A{111})<br>}<br><br>
变量没初始化不能怪函数绑定。
你只是声明了个 A 指针类型变量,肯定会报错啊。
家人们 见鬼了
声明变量但不初始化对象 居然不能调用对象方法
func TestA(t *testing.T) {
var a = new(A)
a.Run()
}
试试呢😜
要么 new, 要么 &A{} 吧
想到了开心的事golang<br>type A struct{}<br><br>func (*A) hello() {<br> fmt.Println("hello, world")<br>}<br><br>func main() {<br> var a *A<br> fmt.Printf("a is nil? %v\n", a == nil)<br> a.hello()<br>}<br>
是可以执行的,a 是一个有类型的 nil ,可以调用这个类型的指针方法
我要调某个结构体的方法,但是我不初始化这个结构体,哎,我就是玩
居然真的可以,涨知识了
Go 实际上编译生成的函数只是在语义层面上跟原始类型绑定,而在之后的代码生成过程中,Go 隐式生成了一个对应的函数,这个新的函数跟老的类型是解耦的,并把对象本身作为第一个参数传入。
举个例子来说,考虑你的测试代码,Go 编译器会生成下面的 stub:go<br>func (*A).Run(a *A) {<br> fmt.Print(a.a)<br>}<br>
这种函数签名是你不能写出来的,因为在用户层面这种语法是 invalid 的,但是你可以调用。尝试在你的 Test 中插入:go<br>func TestA(t *testing.T) {<br> var a *A<br> <a target="_blank" href="http://a.Run" rel="nofollow noopener">a.Run</a>()<br>}<br><br>func TestAnother(t *testing.T) {<br> var a *A<br> (*A).Run(a)<br>}<br>
代码可以正常编译执行,然后报错。
而假如我定义另一个方法,不访问 A 的内部参数,即不依赖 A 的值,你会发现代码能够安全运行,没有任何问题( 16 楼老哥已经有 runnable 了)。
这种情况是不是比较反直觉?而实际上 GCC 和 clang 也是这么做的: https://godbolt.org/z/xb9WWz53x 。当然,如果你打开 undefined behavior santizer ,你会发现编译器报错了,因为标准并没有对这个情况作出规定。
所以是否需要检查 receiver 是不是 nil ?取决于你。如果这个方法你不想 panic ,那你可以检查 if p != nil
并做出恰当报错,但是这会影响大家对于 method call 语义的共识理解(如果 a 为 nil ,我调用它的方法应该 panic 啊?为什么没有 panic ?)。或者直接 let it crash ,也方便查错。
蚌埠住了
go<br> // 以下是定义在结构体值上的方法原型,通过调用结构体类型上定义的函数,传入结构体的值<br> A.echo_A(a) // (_ A)<br> A.echoA(a, "a") // (A) a<br> // A.echo_жA(a) // A.echo_жA 未定义<br> // A.echoжA(a) // A.echoжA 未定义<br> A.setX(a, 4)<br> // A.setY(a, 7) // A.setY 未定义<br> println(a.x) // 0<br>
给你参考一下:
https://github.com/yougg/gonote/blob/main/gogrammar.md#%E6%96%B9%E6%B3%95-method
go 的习惯一般是外围判断的,这算是一种约定
go 的方法你理解成 fun Run(a *A)就好了
cpp<br><br>class A {<br>public:<br> void f1() { cout << "foobar " << this->a;}<br><br>private:<br> int a;<br>};<br><br>int main() {<br> A *a = nullptr;<br> a->f1();<br>}<br>
也不需要判断 this 是不是 NULL 啊,这样是不是好理解多了
这回复楼层不支持 markdown ,真是看的头皮发麻
虽然空值设计确实很麻烦,而且 Go 的 nil 有时候让人感到别扭,但你这个例子……
我更想知道结构体里的 *bool 和 *string 这种东西有没有什么好的解决方案……
struct 指针判断是否为空还是很容易理解,毕竟 c/c++都是如此,golang 不显性的是 interface{} 对象无法通过 != nil 来直接判断是否为空,我也是写了好几个月代码后才发现。
你在(a *A) Run()里面也是可以判断 a 是不是 nil 的。
总有一个人要承担责任,显然调用者是最佳人选。
所以使用 this 指针的时候要不要判断它是不是 null?
要啊! go 又没有异常处理,你得返回一个 error
可以看下这个 /t/873825
这个问题其实挺好玩的 理论上是可以弄成类似 static 函数的行为 但是 go 这么写显然会被打
可以调用方法,但是用了结构体里的东西,没初始化就 panic 了
😀
在Golang(Go语言)中,nil
是一个特殊的标识符,用于表示各种引用类型的零值。它常见于指针、通道(channel)、切片(slice)、映射(map)、函数以及接口类型。理解 nil
的行为对于编写健壮的Go程序至关重要。
-
指针和通道:当指针或通道未初始化时,它们的值默认为
nil
。尝试访问nil
指针或向nil
通道发送/接收数据会引发运行时恐慌(panic)。 -
切片和映射:切片和映射的零值也是
nil
。一个nil
切片的长度和容量都是0,可以安全地追加元素,而无需先检查是否为nil
。对于nil
映射,任何读取或写入操作都会首先触发一个隐式的分配,将映射变为非nil
。 -
接口:接口类型的变量如果未绑定具体类型,则其值为
nil
。可以通过类型断言或类型选择(type switch)来检查接口是否包含nil
或特定类型的值。 -
函数:函数类型的零值也是
nil
,表示没有函数被赋值。调用nil
函数会引发恐慌。
处理 nil
时,建议总是进行显式的检查,特别是在进行解引用或操作前,以避免运行时错误。Go语言的类型安全特性要求开发者对 nil
的处理要格外小心,以确保程序的稳定性和可靠性。希望这能帮助你更好地理解Go语言中 nil
的使用!