zvvq技术分享网

golang:理解 nil 指针和 nil 接口之间的区别(go的

作者:zvvq博客网
导读我正在思考 nil 在 go 中的不同工作方式,以及有时某些东西可以同时为 nil 和非 nil。 这是一个可以是 nil 指针但不是 nil 接口的小例子。让我们来看看这意味着什么。 接口 首先,go 有一

copyright zvvq

我正在思考 nil 在 go 中的不同工作方式,以及有时某些东西可以同时为 nil 和非 nil。

内容来自zvvq

这是一个可以是 nil 指针但不是 nil 接口的小例子。让我们来看看这意味着什么。 zvvq.cn

接口

首先,go 有一个接口的概念,它与一些面向对象语言中的接口类似,但又不完全相同(按照大多数定义,go 不是 OOP)。在 Go 中,接口是一种类型,它定义了另一个类型必须实现才能满足该接口的函数。这允许我们拥有多种具体类型,可以以不同的方式满足接口。

本文来自zvvq

例如,error 是一个具有单一方法的内置接口。看起来像这样:

zvvq好,好zvvq

1 内容来自samhan

2

zvvq好,好zvvq

3

内容来自samhan

输入错误接口{

zvvq.cn

错误字符串

zvvq

}

内容来自samhan

任何想要用作错误的类型都必须有一个名为 Error 的方法,该方法返回一个字符串。例如,可以使用以下代码:

本文来自zvvq

1

zvvq好,好zvvq

2

copyright zvvq

3

zvvq

4

内容来自samhan666

5

zvvq好,好zvvq

6

内容来自zvvq,别采集哟

7

内容来自zvvq

8

zvvq

9

内容来自zvvq,别采集哟

10

copyright zvvq

11

zvvq

12

zvvq.cn

13

内容来自zvvq

14 zvvq.cn

15 内容来自zvvq,别采集哟

16

内容来自zvvq,别采集哟

17 本文来自zvvq

18

内容来自samhan666

19 本文来自zvvq

20

zvvq

21

内容来自samhan666

输入 ErrorMessage 字符串

zvvq.cn

func (em ErrorMessage) Error() 字符串 { 本文来自zvvq

返回字符串(em)

内容来自samhan

} 内容来自samhan

func DoSomething() 错误 {

内容来自samhan

// 尝试做某事,但失败了。

内容来自zvvq,别采集哟

如果某事失败{

zvvq好,好zvvq

var err ErrorMessage = "此失败"

内容来自zvvq

返回错误 zvvq好,好zvvq

}

内容来自zvvq

返回零 copyright zvvq

}

内容来自samhan

函数主() {

内容来自zvvq

错误 := DoSomething() copyright zvvq

如果错误!= nil { 内容来自samhan

恐慌(错误)

内容来自samhan

} zvvq

}

zvvq.cn

请注意,在此示例中,如果出现问题,DoSomething 将返回错误。我们可以使用 ErrorMessage 类型,因为它具有 Error 函数,该函数返回一个字符串,因此实现了错误接口。 zvvq好,好zvvq

如果没有发生错误,我们返回 nil。

指针

在go中,指针指向一个值,但也可以指向无值,这种情况下指针为nil。例如:

本文来自zvvq

1

内容来自samhan

2 内容来自samhan666

3

zvvq好,好zvvq

4

zvvq

5

内容来自zvvq,别采集哟

6 copyright zvvq

7

内容来自zvvq

8

zvvq好,好zvvq

9 内容来自samhan666

var i int = nil 内容来自samhan

函数主() { 内容来自samhan

如果我==零{

内容来自samhan666

j := 5

内容来自samhan

我=&j 本文来自zvvq

} 本文来自zvvq

fmt.Println("i 是", i)

copyright zvvq

}

本文来自zvvq

在这种情况下,i 变量是一个指向 int 的指针。它一开始是一个 nil 指针,直到我们创建一个 int 并将其指向该指针。 本文来自zvvq

”; copyright zvvq

指针和接口

由于用户定义的类型可以附加函数(方法),因此我们也可以拥有指向类型的指针的函数。这是Go中很常见的做法。这也意味着指针也可以实现接口。通过这种方式,我们可以得到一个非 nil 接口的值,但仍然是一个 nil 指针。考虑以下代码: 本文来自zvvq

1

copyright zvvq

2 内容来自zvvq

3 内容来自samhan

4 内容来自samhan

5

内容来自zvvq

6 本文来自zvvq

7 内容来自samhan

8

zvvq好,好zvvq

9

内容来自samhan666

10 内容来自samhan666

11

内容来自zvvq

12 内容来自samhan666

13 copyright zvvq

14

内容来自samhan666

15

内容来自samhan

类型TruthGetter接口{

zvvq.cn

为真() 布尔值

zvvq.cn

} zvvq

func PrintIfTrue(tg TruthGetter) {

内容来自samhan666

如果 tg == nil {

zvvq

fmt.Println("我不知道这是不是真的") zvvq好,好zvvq

返回

内容来自zvvq

} 本文来自zvvq

如果 tg.IsTrue() { 内容来自zvvq

fmt.Println("这是真的") 内容来自zvvq

} 别的 {

本文来自zvvq

fmt.Println("这不是真的")

内容来自zvvq,别采集哟

}

copyright zvvq

} 本文来自zvvq

任何具有 IsTrue() bool 方法的类型都可以传递给 PrintIfTrue,但 nil 也可以。所以,我们可以执行 PrintIfTrue(nil) ,它会打印“我无法判断它是否为真”。 copyright zvvq

我们也可以做一些简单的事情,比如这样: zvvq

1 内容来自zvvq,别采集哟

2 zvvq

3 内容来自samhan

4 内容来自zvvq

5

内容来自zvvq,别采集哟

6

内容来自samhan

7

内容来自zvvq,别采集哟

8

zvvq.cn

9

内容来自samhan666

10

内容来自zvvq,别采集哟

输入 Truthy bool

内容来自samhan

func (ty Truthy) IsTrue() bool {

内容来自zvvq,别采集哟

返回布尔值(ty) 内容来自zvvq,别采集哟

} 内容来自samhan

函数主() {

内容来自samhan

var ty Truthy = true

zvvq.cn

打印如果为真(ty) 内容来自samhan666

}

zvvq好,好zvvq

这将打印“Its true”。 zvvq好,好zvvq

或者,我们可以做一些更复杂的事情,比如:

内容来自samhan

1 zvvq.cn

2

本文来自zvvq

3 内容来自samhan666

4

本文来自zvvq

5

zvvq

6 内容来自samhan

7

本文来自zvvq

8

本文来自zvvq

9

内容来自zvvq,别采集哟

10

内容来自samhan666

输入 TruthyNumber int

内容来自zvvq,别采集哟

func (tn TruthyNumber) IsTrue() bool {

内容来自zvvq

返回 tn > 0

copyright zvvq

}

内容来自zvvq

函数主() {

内容来自samhan666

var tn TruthyNumber = -4

内容来自zvvq,别采集哟

如果为真则打印(tn) 本文来自zvvq

} zvvq.cn

这将打印“这不是真的”。这些示例都不是指针,因此这些类型都不可能出现 nil,但请考虑这一点:

内容来自samhan

1 内容来自zvvq

2

内容来自samhan666

3

内容来自zvvq

4

内容来自samhan666

5

zvvq.cn

6 本文来自zvvq

7

内容来自samhan666

8

内容来自zvvq,别采集哟

类型 TruthyPerson 结构 {

zvvq好,好zvvq

名字字符串 内容来自zvvq

姓氏字符串

zvvq

} 内容来自samhan666

func (tp TruthyPerson) IsTrue() 布尔 { 内容来自zvvq,别采集哟

返回 tp.FirstName != "" && tp.LastName != "" copyright zvvq

}

zvvq

在这种情况下,TruthyPerson 没有实现 TruthGetter,但 TruthyPerson 实现了。所以,这应该有效:

内容来自zvvq

1 内容来自zvvq,别采集哟

2 本文来自zvvq

3

内容来自samhan666

4

内容来自zvvq,别采集哟

func main() { zvvq

tp := &TruthyPerson{"乔恩", "格雷迪"}

copyright zvvq

如果为真则打印(tp)

zvvq好,好zvvq

} 内容来自samhan

这是可行的,因为 tp 是一个指向 TruthyPerson 的指针。然而,如果指针为零,我们就会感到恐慌。 zvvq

1

内容来自samhan

2 zvvq好,好zvvq

3 内容来自samhan666

4

zvvq好,好zvvq

func main() { 本文来自zvvq

var tp TruthyPerson

zvvq

如果为真则打印(tp)

内容来自samhan

} copyright zvvq

这会恐慌。但是,PrintIfTrue 中不会发生恐慌。您可能会认为这很好,因为 PrintIfTrue 检查是否为 nil。但是,问题就在这里。它正在针对 TruthGetter 检查 nil。换句话说,它检查的是 nil 接口,而不是 nil 指针。在 func (tp TruthyPerson) IsTrue() bool 中,我们不检查 nil。在 go 中,我们仍然可以在 nil 指针上调用方法,因此恐慌发生在那里。修复实际上非常简单。

内容来自zvvq

1 内容来自samhan

2 zvvq.cn

3

内容来自samhan666

4 zvvq.cn

5 copyright zvvq

6

内容来自zvvq,别采集哟

func (tp TruthyPerson) IsTrue() bool {

内容来自zvvq,别采集哟

如果 tp == nil {

内容来自samhan666

返回错误

zvvq好,好zvvq

}

zvvq.cn

返回 tp.FirstName != "" && tp.LastName != "" 本文来自zvvq

} 内容来自zvvq,别采集哟

现在,我们检查 PrintIfTrue 中是否有 nil 接口以及 func (tp TruthyPerson) IsTrue() bool 中是否有 nil 指针。现在它会打印“这不是真的”。我们可以看到所有这些代码都在这里工作。 zvvq

奖励:通过反射立即检查两个 nil

通过反射,我们可以对 PrintIfTrue 做一些小的改变,以便它可以检查 nil 接口和 nil 指针。代码如下:

zvvq

1 本文来自zvvq

2 zvvq

3

内容来自samhan

4

内容来自zvvq

5

内容来自samhan

6

内容来自zvvq,别采集哟

7 zvvq

8

内容来自samhan

9 内容来自samhan

10 内容来自samhan

11

zvvq好,好zvvq

12 本文来自zvvq

13

内容来自zvvq

14

内容来自zvvq,别采集哟

15 内容来自zvvq,别采集哟

16

copyright zvvq

17

zvvq.cn

18

copyright zvvq

19 内容来自samhan

func PrintIfTrue(tg TruthGetter) {

zvvq.cn

如果 tg == nil {

内容来自zvvq,别采集哟

fmt.Println("我不知道这是不是真的") 内容来自samhan666

返回 zvvq

}

zvvq.cn

val := 反射.ValueOf(tg) 内容来自zvvq

k := val.Kind() zvvq

if (k ==reflect.Pointer || k ==reflect.Chan || k ==reflect.Func || k ==reflect.Map || k ==reflect.Slice) && val.IsNil() {

zvvq好,好zvvq

fmt.Println("我不知道这是不是真的") 内容来自samhan666

返回 zvvq

} zvvq

如果 tg.IsTrue() {

内容来自samhan

fmt.Println("这是真的")

zvvq好,好zvvq

} 别的 {

内容来自zvvq,别采集哟

fmt.Println("这不是真的")

内容来自samhan

} 内容来自samhan666

}

zvvq.cn

在这里,我们像以前一样首先检查 nil 接口。接下来,我们使用反射来获取类型。除了指针之外,chan、func、map 和 slice 也可以为 nil,因此我们检查该值是否是这些类型之一,如果是,则检查它是否为 nil。如果是,我们也会返回“我无法判断这是否是真的”消息。这可能是也可能不是您想要的,但它是一个选择。通过这个改变,我们可以做到这一点:

内容来自samhan

1 copyright zvvq

2 zvvq好,好zvvq

3 zvvq.cn

4

zvvq好,好zvvq

func main() { 内容来自zvvq

var tp TruthyPerson

zvvq好,好zvvq

如果为真则打印(tp)

本文来自zvvq

}

zvvq.cn

您有时可能会看到更简单的建议,例如: copyright zvvq

1

内容来自samhan

2 内容来自samhan666

3 本文来自zvvq

4

内容来自zvvq

5

内容来自zvvq

//不要这样做

zvvq

if tg == nil && Reflect.ValueOf(tg).IsNil() { zvvq好,好zvvq

fmt.Println("我不知道这是不是真的") 内容来自samhan

返回 zvvq.cn

}

zvvq

这效果不好有两个原因。首先,使用反射时会产生性能开销。如果您可以避免使用反射,那么您可能应该这样做。如果我们先检查nil接口,如果是nil接口,我们就不必使用反射。 zvvq

第二个原因是如果值的类型不是可以为 nil 的类型,reflect.Value.IsNil() 会发生恐慌。这就是我们添加此类检查的原因。如果我们没有检查 Kind,那么我们会对 Truthy 和 TruthyNumber 类型感到恐慌。 zvvq好,好zvvq

因此,只要我们确保首先检查类型,现在就会打印“我无法判断它是否为真”,而不是“这不是真的”。根据您的观点,这可能是一种改进。这是进行此更改的完整代码。 zvvq.cn

这最初发表在 Dans Musings copyright zvvq

以上就是golang:理解 nil 指针和 nil 接口之间的区别的详细内容,更多请关注其它相关文章!

内容来自samhan