索引: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. 解决前端向后端请求静态资源的问题(基于express框架)

    请求js,css,image资源: 前端 <script src='后端url/assets/js/xxx.js'>,<link href='后端url/assets/css/xxx ...

  2. 解决git报错

    解决git报错:fatal: unable to access "https://github.com/.../.git/" 1.在git中执行(记得分开执行) git confi ...

  3. [PostgreSql]生产级别数据库安装要考虑哪些问题?

    大家好,我是字母哥(coder)! 我让公司的小伙伴写一个生产级别的PostgreSQL的安装文档,结果他和我说:"不是用一个命令就能安装好么?还用写文档么?".我知道他想说的是这 ...

  4. C语言重定向输入:txt文件内容是中文,重定向输入显示乱码的原因

    一.txt文件中的内容是中文,重定向输入显示乱码原因: 是因为文本文件的编码和和编译器的不一致导致的.我文本文件用的编码是UTF-8,而编译器是ANSI,不匹配,所以输出乱码.文本另存为时把编码改为A ...

  5. 并发编程二、CPU多级缓存架构与MESI协议的诞生

    ​前言: 文章内容:线程与进程.线程生命周期.线程中断.线程常见问题总结 本文章内容来源于笔者学习笔记,内容可能与相关书籍内容重合 偏向于知识核心总结,非零基础学习文章,可用于知识的体系建立,核心内容 ...

  6. 【Maven】Maven的安装和配置

    1.Maven的下载 方式一: 官网:Maven – Welcome to Apache Maven  尽量下载3.5版本,我个人3.8版本从来没用配置成功过. 方式二: 我用的是3.5版本,下载3. ...

  7. Linux 定时器介绍

    以下内容为本人的著作,如需要转载,请声明原文链接微信公众号「englyf」https://www.cnblogs.com/englyf/p/16651865.html 曾经常去沙县小吃,就为了蹭上一碗 ...

  8. volatile 函数影响子查询提升

    我们知道 volatile 函数会影响SQL的执行性能,比如:volatile 类型函数无法建函数索引.volatile 函数针对每条记录都要执行一次.本篇的例子主要讲述 volatile 类型的函数 ...

  9. 自定义View5 -塔防小游戏:第二篇防御塔随意放置

    第一篇:一个防御塔+多个野怪(简易版) 第二篇:防御塔随意放置 自定义View,处理事件分发,up,move,down. 第三篇:防御塔随意放置+多组野怪 第四篇:多波野怪 第五篇:杀死野怪获得金币 ...

  10. Liquibase-数据库版本管理控制

    1. 简介 Liquibase是一个用于跟踪.管理和应用数据库变化的开源的数据库重构工具.它将所有数据库的变化(包括结构和数据)都保存在XML文件中,便于版本控制. Liquibase使参与应用程序发 ...