索引:https://waterflow.link/articles/1666534616841

我们先看一个简单的例子,我们自定义一个错误,用来把多个错误放在一起输出:

type CustomError struct {
errors []string
} func (c *CustomError) Add(err string) {
c.errors = append(c.errors, err)
} func (c *CustomError) Error() string {
return strings.Join(c.errors, ";")
}

因为实现了Error() string方法,所以它实现了error接口。

现在我们要实现一个添加课件的功能,但是添加之前需要验证参数的合法性,所以我们创建了一个Validate方法,我们可能会这么写:

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 // 1
if c.Name == "" { // 2
m = &CustomError{}
m.Add(errors.New("课件名不能为空"))
}
if c.Code == "" { // 3
if m == nil {
m = &CustomError{}
}
m.Add(errors.New("课件编号不能为空"))
} return m // 4
} func main() {
m := Courseware{
Name: "多媒体课件",
Code: "CW330",
}
if err := m.Validate(); err != nil {
fmt.Println("valid err: ", err)
}
}

看上去好像一点问题都没有:

  1. 定义一个CustomError类型的指针
  2. 如果Name为空,初始化m,调用Add方法把错误添加到CustomError.errors
  3. 如果Code为空,如果m还没有初始化,先初始化,调用Add方法把错误添加到CustomError.errors
  4. 最后返回自定义错误

但是当我们执行上面的代码时,会发现结果并不是我们想要的:

go run 8.go
valid err: <nil>

我们发现居然走到了打印错误的判断里,但是打印出来的错误居然是一个nil。

在 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

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类型的错误。

golang中的nil接收器的更多相关文章

  1. 如何理解golang中的nil

    nil的奇怪行为 刚接触golang时,发现nil在不同的上下文,行为表现是不同的,并且和其他语言中的表现,也不大相同 实例1:输入true, true, false,不符合传递性 func main ...

  2. [Go] 理解 golang 中的 nil

    nil是什么 相信写过Golang的程序员对下面一段代码是非常非常熟悉的了: if err != nil { // do something.... } 当出现不等于nil的时候,说明出现某些错误了, ...

  3. 【荐】详解 golang 中的 interface 和 nil

    golang 的 nil 在概念上和其它语言的 null.None.nil.NULL一样,都指代零值或空值.nil 是预先说明的标识符,也即通常意义上的关键字.在 golang 中,nil 只能赋值给 ...

  4. Golang中的坑二

    Golang中的坑二 for ...range 最近两周用Golang做项目,编写web服务,两周时间写了大概五千行代码(业务代码加单元测试用例代码).用Go的感觉很爽,编码效率高,运行效率也不错,用 ...

  5. Golang 中的坑 一

    Golang 中的坑 短变量声明  Short variable declarations 考虑如下代码: package main import ( "errors" " ...

  6. google的grpc在golang中的使用

    GRPC是google开源的一个高性能.跨语言的RPC框架,基于HTTP2协议,基于protobuf 3.x,基于Netty 4.x. 前面写过一篇golang标准库的rpc包的用法,这篇文章接着讲一 ...

  7. Golang中Struct与DB中表字段通过反射自动映射 - sqlmapper

    Golang中操作数据库已经有现成的库"database/sql"可以用,但是"database/sql"只提供了最基础的操作接口: 对数据库中一张表的增删改查 ...

  8. Golang中使用lua进行扩展

    前言 最近在项目中需要使用lua进行扩展,发现github上有一个用golang编写的lua虚拟机,名字叫做gopher-lua.使用后发现还不错,借此分享给大家. 数据类型 lua中的数据类型与go ...

  9. golang中Context的使用场景

    golang中Context的使用场景 context在Go1.7之后就进入标准库中了.它主要的用处如果用一句话来说,是在于控制goroutine的生命周期.当一个计算任务被goroutine承接了之 ...

随机推荐

  1. B/S结构通信系统原理

    本文介绍JavaWeb的B/S结构通信原理 概念:        Javaweb中B/S架构是一种系统架构形式,这里的B是Browser(浏览器),S是Server(服务器),是一种系统的架构形式,有 ...

  2. 【问题解决】npm ERR! code EINTEGRITY

    问题说明 Jenkins构建前端安装依赖报错: npm ERR! code EINTEGRITY 11:05:42 npm ERR! sha512-IJy2B5Ot9wIAGwjSKF94+8yhVC ...

  3. spring项目中starter包的原理,以及自定义starter包的使用

    MAVEN项目中starter的原理 一.原始方式 我们最早配置spring应用的时候,必须要经历的步骤:1.pom文件中引入相关的jar包,包括spring,redis,jdbc等等 2.通过pro ...

  4. eclipse 统一设置编码_项目工程统一设置成utf8编码_eclipse代码规范

    在做项目的时候文件有的时候编码不同一 经常出现乱码,eclipse统一设置编码 可以解决项目编码混乱的问题, 设置eclipse java,jsp,css,js文件编码的方法如下: 1.在工具栏中点击 ...

  5. 来点基础的练习题吧,看见CSDN这类基础的代码不多

    来点基础的练习题吧,看见CSDN这类基础的代码不多 //正三角形 void ex03(){ int i,k=0, rows, space; printf("请输入三角形的层次:") ...

  6. Deepin20 LNMP环境搭建(又一个瞎折腾的经历)

    背景 最近头脑发热,想安装个Linux系统来办工,所以安装了Deepin20的系统,折腾了一天的LNMP的环境,记录一下瞎折腾的过程. lnmp 是一个常见的web开发的环境 nginx 如果安装过可 ...

  7. linux下搭建ftp文件服务器

    linux下搭建ftp文件服务器 一.搭建步骤(以在centos7中搭建为例) 1.首先检查一下系统中是否已经安装了vsftpd软件 # 查看是否安装vsftpd rpm -q vsftpd rpm ...

  8. 7、System类

    System类 常见方法 exit 退出当前程序 System.out.println("ok1"); //1. exit(0) 表示程序退出 //2. 0 表示一个状态,正常的状 ...

  9. Django 使用Pycharm 创建工程

    一.Pycharm 创建Django 工程 事实上,我们一般不使用命令行,而是直接在Pycharm 中创建Django 项目. Pycharm 是进行Django 开发的最佳 IDE,请大家自行安装, ...

  10. 【学习笔记】注意力机制(Attention)

    前言 这一章看啥视频都不好使,啃书就完事儿了,当然了我也没有感觉自己学的特别扎实,不过好歹是有一定的了解了 注意力机制 由于之前的卷积之类的神经网络,选取卷积中最大的那个数,实际上这种行为是没有目的的 ...