golang中的nil接收器
索引: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)
}
}
看上去好像一点问题都没有:
- 定义一个CustomError类型的指针
- 如果Name为空,初始化m,调用Add方法把错误添加到CustomError.errors
- 如果Code为空,如果m还没有初始化,先初始化,调用Add方法把错误添加到CustomError.errors
- 最后返回自定义错误
但是当我们执行上面的代码时,会发现结果并不是我们想要的:
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接收器的更多相关文章
- 如何理解golang中的nil
nil的奇怪行为 刚接触golang时,发现nil在不同的上下文,行为表现是不同的,并且和其他语言中的表现,也不大相同 实例1:输入true, true, false,不符合传递性 func main ...
- [Go] 理解 golang 中的 nil
nil是什么 相信写过Golang的程序员对下面一段代码是非常非常熟悉的了: if err != nil { // do something.... } 当出现不等于nil的时候,说明出现某些错误了, ...
- 【荐】详解 golang 中的 interface 和 nil
golang 的 nil 在概念上和其它语言的 null.None.nil.NULL一样,都指代零值或空值.nil 是预先说明的标识符,也即通常意义上的关键字.在 golang 中,nil 只能赋值给 ...
- Golang中的坑二
Golang中的坑二 for ...range 最近两周用Golang做项目,编写web服务,两周时间写了大概五千行代码(业务代码加单元测试用例代码).用Go的感觉很爽,编码效率高,运行效率也不错,用 ...
- Golang 中的坑 一
Golang 中的坑 短变量声明 Short variable declarations 考虑如下代码: package main import ( "errors" " ...
- google的grpc在golang中的使用
GRPC是google开源的一个高性能.跨语言的RPC框架,基于HTTP2协议,基于protobuf 3.x,基于Netty 4.x. 前面写过一篇golang标准库的rpc包的用法,这篇文章接着讲一 ...
- Golang中Struct与DB中表字段通过反射自动映射 - sqlmapper
Golang中操作数据库已经有现成的库"database/sql"可以用,但是"database/sql"只提供了最基础的操作接口: 对数据库中一张表的增删改查 ...
- Golang中使用lua进行扩展
前言 最近在项目中需要使用lua进行扩展,发现github上有一个用golang编写的lua虚拟机,名字叫做gopher-lua.使用后发现还不错,借此分享给大家. 数据类型 lua中的数据类型与go ...
- golang中Context的使用场景
golang中Context的使用场景 context在Go1.7之后就进入标准库中了.它主要的用处如果用一句话来说,是在于控制goroutine的生命周期.当一个计算任务被goroutine承接了之 ...
随机推荐
- 解决前端向后端请求静态资源的问题(基于express框架)
请求js,css,image资源: 前端 <script src='后端url/assets/js/xxx.js'>,<link href='后端url/assets/css/xxx ...
- 解决git报错
解决git报错:fatal: unable to access "https://github.com/.../.git/" 1.在git中执行(记得分开执行) git confi ...
- [PostgreSql]生产级别数据库安装要考虑哪些问题?
大家好,我是字母哥(coder)! 我让公司的小伙伴写一个生产级别的PostgreSQL的安装文档,结果他和我说:"不是用一个命令就能安装好么?还用写文档么?".我知道他想说的是这 ...
- C语言重定向输入:txt文件内容是中文,重定向输入显示乱码的原因
一.txt文件中的内容是中文,重定向输入显示乱码原因: 是因为文本文件的编码和和编译器的不一致导致的.我文本文件用的编码是UTF-8,而编译器是ANSI,不匹配,所以输出乱码.文本另存为时把编码改为A ...
- 并发编程二、CPU多级缓存架构与MESI协议的诞生
前言: 文章内容:线程与进程.线程生命周期.线程中断.线程常见问题总结 本文章内容来源于笔者学习笔记,内容可能与相关书籍内容重合 偏向于知识核心总结,非零基础学习文章,可用于知识的体系建立,核心内容 ...
- 【Maven】Maven的安装和配置
1.Maven的下载 方式一: 官网:Maven – Welcome to Apache Maven 尽量下载3.5版本,我个人3.8版本从来没用配置成功过. 方式二: 我用的是3.5版本,下载3. ...
- Linux 定时器介绍
以下内容为本人的著作,如需要转载,请声明原文链接微信公众号「englyf」https://www.cnblogs.com/englyf/p/16651865.html 曾经常去沙县小吃,就为了蹭上一碗 ...
- volatile 函数影响子查询提升
我们知道 volatile 函数会影响SQL的执行性能,比如:volatile 类型函数无法建函数索引.volatile 函数针对每条记录都要执行一次.本篇的例子主要讲述 volatile 类型的函数 ...
- 自定义View5 -塔防小游戏:第二篇防御塔随意放置
第一篇:一个防御塔+多个野怪(简易版) 第二篇:防御塔随意放置 自定义View,处理事件分发,up,move,down. 第三篇:防御塔随意放置+多组野怪 第四篇:多波野怪 第五篇:杀死野怪获得金币 ...
- Liquibase-数据库版本管理控制
1. 简介 Liquibase是一个用于跟踪.管理和应用数据库变化的开源的数据库重构工具.它将所有数据库的变化(包括结构和数据)都保存在XML文件中,便于版本控制. Liquibase使参与应用程序发 ...