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承接了之 ...
随机推荐
- MySQL之JDBC编程增删改查
MySQL之JDBC 一.JDBC是什么 Java DatabaseConnectivity (java语言连接数据库) 二.JDBC的本质 JDBC是SUN公司制定的一套接口(interface). ...
- 【Meetup回顾】Apache DolphinScheduler在联通的实践和二次开发经验分享
在由 openLooKeng 社区主办,Apahce DolphinScheduler社区.Apache Pulsar 社区.示说网协办的联合 Meetup 上,来自联通数字科技的王兴杰老师分享了Do ...
- 基于 Sequelize.js + Express.js 开发一套 Web 后端服务器
什么是 Sequelize 我们知道 Web 应用开发中的 Web 后端开发一般都是 Java.Python.ASP.NET 等语言.十年前,Node.js 的出现使得原本仅限于运行在浏览器中的 Ja ...
- python与pycharm的安装与“试用”
python与pycharm的安装与"试用" 一.python解释器安装与启动 python解释器的安装 1.打开文件安装包运行页面 #python3.8 2.选择Customiz ...
- 【NOI P模拟赛】混凝土粉末(整体二分)
题面 样例输入 5 8 1 1 4 2 2 3 1 2 3 3 1 2 5 1 2 3 3 2 5 2 2 1 2 2 1 3 样例输出 1 0 4 0 1 0 样例解释 题解 比这道题简单了不知多少 ...
- 弹簧高跷题解---双向DP---DD(XYX)的博客
三 . 弹簧高跷 时间限制: 1 Sec 内存限制: 128 MB 题目描述.输入.输出 ----------- 方法 这道题用DP是可以解决的.因为每一次跳跃都与前一次跳跃有关, ...
- Sirni题解(最小生成树,埃氏筛)(继 Liang-梁)
目录 前言 题意 思路 一些建议 前言 本篇是对Liang-梁的Sirni(最小生成树,埃氏筛)的后继博客. 通篇原文:https://blog.csdn.net/qq_37555704/articl ...
- 巧用 transition 实现短视频 APP 点赞动画
在各种短视频界面上,我们经常会看到类似这样的点赞动画: 非常的有意思,有意思的交互会让用户更愿意进行互动. 那么,这么有趣的点赞动画,有没有可能使用纯 CSS 实现呢?那当然是必须的,本文,就将巧妙的 ...
- RTSP播放器开发填坑之道
好多开发者提到,在目前开源播放器如此泛滥的情况下,为什么还需要做自研框架的RTSP播放器,自研和开源播放器,到底好在哪些方面?以下大概聊聊我们的一点经验,感兴趣的,可以关注 github: 1. 低延 ...
- spark 读取hive 计算后写入hive
package com.grady import org.apache.spark.SparkConf import org.apache.spark.sql.{DataFrame, Row, Spa ...