在go语言中nil接收器浅析及代码示例

2023-06-01 00:00:00 示例 接收器 浅析

在go中,我们必须知道指针接收器可以为nil,我们看一个简单的例子:

package main

import (
   "fmt"
)

type Demo struct {
}

func (d *Demo) Print() string {
   return "demo"
}

func main() {
   var d *Demo
   fmt.Println(d)
   fmt.Println(d.Print())
}

输出:

go run 8.go
<nil>
demo

上面代码被初始化为nil,但是这段代码可以正常运行。

说明nil指针也可以作为接收器。

其实上面的Print方法等价于:

func Print(d *Demo) string {
   return "demo"
}

因为将 nil 指针传递给函数是有效的。 所以使用 nil 指针作为接收器也是有效的。

我们继续回到上面的自定义错误。

m 被初始化为指针的零值:nil。

如果所有验证都通过,return 语句返回的结果不是 nil,而是一个 nil 指针。 


因为 nil 指针是一个有效的接收器,所以将结果转换为 error 接口不会产生 nil 值。

所以我们虽然返回了一个 nil 指针,但是转换为 error 接口时并不是一个 nil 的接口

(虽然是 nil 指针,但是是*CustomError 类型,并实现了 error)。


要解决这个问题,我们只要直接返回 nil 值,不返回 nil 的指针:


package main

import (
"errors"
"fmt"
"strings"
)

type CustomError struct {
errors []string
}

func (c *CustomError) Add(err error) {
c.errors = append(c.errors, err.Error())
}

func (c *CustomError) Error() string {
return strings.Join(c.errors, ";")
}

type Courseware struct {
Name string
Code string
}

func (c *Courseware) Validate() error {
var m *CustomError
if c.Name == "" {
m = &CustomError{}
m.Add(errors.New("课件名不能为空"))
}
if c.Code == "" {
if m == nil {
m = &CustomError{}
}
m.Add(errors.New("课件编号不能为空"))
}

// 这里如果m指针为nil,直接返回nil
if m == nil {
return nil
}

return m
}

func main() {
m := Courseware{
Name: "多媒体课件",
Code: "CW330",
}

if err := m.Validate(); err != nil {
fmt.Println("valid err: ", err)
}
}

或者我们直接返回*CustomError 类型的错误:

package main

import (
"errors"
"fmt"
"strings"
)

type CustomError struct {
errors []string
}

func (c *CustomError) Add(err error) {
c.errors = append(c.errors, err.Error())
}

func (c *CustomError) Error() string {
return strings.Join(c.errors, ";")
}

type Courseware struct {
Name string
Code string
}

// 返回*CustomError
func (c *Courseware) Validate() *CustomError {
var m *CustomError
if c.Name == "" {
m = &CustomError{}
m.Add(errors.New("课件名不能为空"))
}
if c.Code == "" {
if m == nil {
m = &CustomError{}
}
m.Add(errors.New("课件编号不能为空"))
}

return m
}

func main() {
m := Courseware{
Name: "多媒体课件",
Code: "CW330",
}

if err := m.Validate(); err != nil {
fmt.Println("valid err: ", err)
}
}

但这并不是可取的,为了扩展我们实现了error接口,

也需要返回error类型的错误。

相关文章