Go日常分享 - error类型是指针类型吗?

背景

这个问题的产生来源于小泉在开发rpc接口时返回error遇到的问题,开发时想在defer里对err进行最终的统一处理赋值,发现外层接收一直都未生效。问题可以简化为成下面的小demo。

func returnError() error {var err errordefer func() {//err = errors.New("defer error")err = nil}()err = errors.New("test error")return err
}
func main() {fmt.Printf("return error : %v\n", returnError())
}

这个函数会输出什么呢?大家可以自己试一试。

在问题实验之前,我们先介绍一下本文可能会涉及到的一些Go的基本概念。

文章涉及代码部分已放置github。

github地址:go-demo

指针receiver

interface可以理解为方法的集合体,它是某一类对象的行为表现和Java中的interface如出一辙,而实现该interface所有方法的对象(结构体)都可以作为该interface类型,即实现该interface。以如下Box接口以及BigBox结构体为例来作为该节内容说明。

type Box interface {Color() stringColor2() string
}type BigBox struct {ColorStr stringVolume   float64
}func (b BigBox) Color() string {return b.ColorStr
}func (b BigBox) Color2() string {return b.ColorStr
}// 测试
func (b BigBox) SetColor(c string) BigBox {b.ColorStr = creturn b
}func (b *BigBox) SetColor2(c string) {b.ColorStr = c
}func main() {box := BigBox{}boxCopy := box.SetColor("red")var box2 Box = boxfmt.Printf("after SetColor return box color: %v\n", box2.Color())fmt.Printf("after SetColor return boxCopy color: %v\n", boxCopy.Color())var box3 Box = &boxbox.SetColor2("red")fmt.Printf("after SetColor2 return box color: %v\n", box3.Color())
}

Box接口内方法由BigBox结构体实现,同时定义了两个方法SetColorSetColor2

  • SetColorBigBox作为receiver,同时返回值为BigBox类型
  • SetColor2*BigBox即指针作为receiver。

main函数中我们定义box作为BigBox实例对象,并分别使用SetColorSetColor2ColorStr进行赋值,同时SetColor时返回赋值后的box称之为boxCopy对象,在打印值时会发现:

对于box对象来说,SetColor并没有生效,而boxCopy对象生效了。这是因为在调用非指针receiver接收的方法时Go语言会对box进行拷贝,在赋值时并非对box对象进行了赋值,因此在测试时,boxCopy对象的Color值赋值成功,而box的未成功。而指针型receiver则不会进行拷贝,而是直接赋值。

同时你可以看到对box2、box3的赋值的不同(对象,指针对象),但是都可以作为Box对象的实例

defer介绍

defer这里小泉只做些简短介绍(在学了,在学了),它主要是起到延迟调用的作用,defer关键字的写入触发方式是按照栈的方式,写入用先到后入栈,出栈则由后到先出栈。

其调用链路如下:

image.png

return先完成赋值语句,再去执行defer,最后再执行一次return返回函数调用处。

return语句如果赋值非指针类型,则会发生值拷贝。

error

接下来我们看下error类型。error类型是Go语言中最常用到的数据类型,无时无刻,随时随地,我们都要if err != nil所以我们来看下error的结构。

type error interface {Error() string
}

error本身只是一个接口。我们所使用的error都是结构体通过实现Error()方法从而可以作为error被使用。

再看一下我们最常见的errors.New方法底层代码,也正是由于这个方法,我们会对最初的问题产生分歧。

package errors// New returns an error that formats as the given text.
// Each call to New returns a distinct error value even if the text is identical.
func New(text string) error {return &errorString{text}
}// errorString is a trivial implementation of error.
type errorString struct {s string
}func (e *errorString) Error() string {return e.s
}

我们常用的error大多数都会通过errors.New()方法去创建error,而此时返回的error&errorString,所以很多人会认为此时拿到error类型应该是指针呀。

但根据前文interface相关的分析,这里其实做了一层转换,此时针对类型而言,函数调用返回值类型就是error类型,而不是*errorstring,即非指针类型

问题

在做了诸多前置解释之后,我们来做点小demo实验吧。

实验

func returnError() error {var err errordefer func() {err = nil}()err = errors.New("test error")return err
}func returnError2()(err error) {defer func() {err = nil}()err = errors.New("test error")return err
}func returnErrorPtr() *error {var err errordefer func() {err = nil}()err = errors.New("test error")return &err
}
func main() {fmt.Printf("return error by return: %v\n", returnError())fmt.Printf("return error by err return: %v\n", returnError2())fmt.Printf("return error by ptr: %v\n", *returnErrorPtr())
}

结果

解释

returnError return非指针类型,发生浅拷贝赋值完成,然后defer执行去修改局部变量,对return赋值的变量无影响。

returnError2 已经声明变量err了,因此returndefer函数内操作的都是都是err变量。

returnErrorPtr 指针型变量,返回值的本身就是地址,因此同样操作的都是指针地址下的内容。

总结

小泉自我感受,Go语言很多时候在变量赋值方面会帮开发去做浅拷贝操作,所以一般最好指针实例化对象(inteface、结构体类型),同时记得return赋值非指针对象(包括结构体、interface)会发生拷贝逻辑,所以对局部变量的修改都不会影响返回值的结果哦。

以及最后一点,error也不是指针类型!!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/32729.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

NSIS 入门教程 (三)

引言 在教程的第二部分中,我们为安装程序增加了一个卸载程序,并查看了一些其他的向导页面以及安装部分的选择。第三部分的目标是使安装程序的外观更加现代化。 更现代的外观 为了给安装程序一个更现代的外观,我们要启用现代用户界面。要提…

【栈和队列】

目录 1,栈(Stack) 1.1 概念 1.2 栈的使用 1.3 栈的模拟实现 1.4 栈的应用场景 1.5 概念区分 1.6 使用链表来实现栈 2, 队列(Queue) 2.1 概念 2.2 队列的使用 2.3 队列模拟实现 3,双端队列 (Deque) 4&…

【计算机组成原理】部分题目汇总

计算机组成原理 部分题目汇总 一. 简答题 RISC和CICS 简要说明,比较异同 RISC(精简指令集)注重简单快速的指令执行,使用少量通用寄存器,固定长度指令,优化硬件性能,依赖软件(如编译…

递归调用,将源路径下所有文件文件夹复制到目标路径中.

其实代码demo很简洁&#xff0c;只是逻辑有点绕&#xff0c;主要是要一层一层调用自己&#xff0c;要清楚当前是第几层调用&#xff0c;及递归调用时进的点和出的点在哪儿&#xff0c;一切就清晰明了了。 /// <summary>/// 删除指定目录下面的所有文件和文件夹/// </s…

C++学习合集

#整理到一块&#xff0c;方便查东西&#xff0c;顺便补充一些之前没有学习到的东西# 变量 char--1字节 short--2字节 int-4字节 long--4字节 long long(int)--8字节&#xff1b;准确来说变量的大小取决于编译器&#xff0c;1字节8个二进制位&#xff0c;其中最高位为符号位…

基于Java的火车订票管理系统【附源码】

火车订票管理登录 摘要&#xff1a;随着我国铁路交通的不断发展&#xff0c;简单的窗口售票模式已经不能满足方便人们出行的目的。采用先进的网络技术开发出方便快捷的火车票订票系统是现代客运业务发展的必然需求。本次设计的火车票订票系统通过访问主页&#xff0c;可以实现…

算法训练与程序竞赛题目集合(L4)

目录 L4-103 就不告诉你 输入格式&#xff1a; 输出格式&#xff1a; 输入样例&#xff1a; 输出样例&#xff1a; L4-104 Wifi密码 输入格式&#xff1a; 输出格式&#xff1a; 输入样例&#xff1a; 输出样例&#xff1a; L4-105 冠军魔术 输入格式&#xff1a; …

Flutter TIM 项目配置

目录 1. 设计说明 2. 参考资料索引 Flutter SDK 服务端 Rest API 腾讯后台 其他 3. TIM 整体架构 第一部分&#xff1a;APP 端 第二部分&#xff1a;腾讯服务器 第三部分&#xff1a;三方服务 第四部分&#xff1a;你自己的服务器 4. TIM SDK 集成 TUIK 含 UI 集成…

物联网系统运维——数据库部署

一.MySQL 1.概要 MySQL是一种关联数据库管理系统&#xff0c;关联数据:而不是将所有数据放在一个大仓库内&#xff0c;这样就增加了速度并提高了灵活性库将数据保存在不同的表中。性能高、成本低、可靠性好&#xff0c;已经成为最流行的开源数据库。 二.MySQL安装与配置 1. …

DataStructure.时间和空间复杂度

时间和空间复杂度 【本节目标】1. 如何衡量一个算法的好坏2. 算法效率3. 时间复杂度3.1 时间复杂度的概念3.2 大O的渐进表示法3.3 推导大O阶方法3.4 常见时间复杂度计算举例3.4.1 示例13.4.2 示例23.4.3 示例33.4.4 示例43.4.5 示例53.4.6 示例63.4.7 示例7 4.空间复杂度4.1 示…

redis-实战篇(8)达人探店

8、达人探店 8.1、达人探店-发布探店笔记 发布探店笔记 探店笔记类似点评网站的评价&#xff0c;往往是图文结合。对应的表有两个&#xff1a; tb_blog&#xff1a;探店笔记表&#xff0c;包含笔记中的标题、文字、图片等 tb_blog_comments&#xff1a;其他用户对探店笔记的…

网格处理库 pmp-library 编译及应用笔记 -- 已全部解决√

多边形网格处理库Polygon Mesh Processing Library&#xff0c;简称pmp-library的 编译及应用笔记 – 已全部解决√ 官网&#xff1a;https://www.pmp-library.org/index.html 代码&#xff1a;https://github.com/pmp-library/pmp-library 平台&#xff1a;Ubuntu1 20.04&…

Bandzip:打破压缩界限,文件管理更高效

名人说&#xff1a;&#xff1a;一点浩然气&#xff0c;千里快哉风。 ——苏轼 创作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 目录 一、软件介绍1、Bandzip2、核心特点 二、下载安装1、下载2、安装 三、使用方法 很高兴…

C语言中操作符详解(二)

OK&#xff0c;今天继续为诸君带来有关C语言中操作符的讲解 一 . 位操作符 C语言中的位操作符我相信大家并不陌生&#xff0c;我们在之前就已经接触过了一些 位操作符&#xff08;位操作符的操作数只能是整数&#xff09;&#xff1a; &#xff08;1&#xff09;& &…

Origin较好用的科研绘图软件

推荐自己也在用的科研绘图软件Origin图所示&#xff1a; 图1 图2 图3

WMS项目测试点

这里写目录标题 最后附有图片 仓库系统 仓库 / 库区 仓库 新增仓库 编号 必填校验 字段长度校验 20为字符 数据类型校验 名称 必填校验 字段长度校验 20为字符 数据类型校验 备注 填写备注校验 字符长度限制 不填写备注校验 新增仓库之后是否可以通过查询仓库名称和仓库编号查询…

poi生成的excel,输入数字后变成1.11111111111111E+23

poi版本4.1.2 生成excel后&#xff0c;单元格输入数字&#xff0c;过长的话变成这样 解决&#xff1a;生成的时候设置单元格格式为文本格式 import org.apache.poi.ss.usermodel.*; import org.apache.poi.xssf.usermodel.XSSFWorkbook;import java.io.FileOutputStream; imp…

数据结构与算法笔记:基础篇 - 初始动态规划:如何巧妙解决“双十一”购物时的凑单问题?

概述 淘宝的 “双十一” 购物节有各种促销活动&#xff0c;比如 “满 200 元减 50元”。假设你女朋友购物车中有 n 个&#xff08;n > 100&#xff09;想买的商品&#xff0c;它希望从里面选几个&#xff0c;在凑够满减条件的前提下&#xff0c;让选出来的商品价格总和最长…

学习笔记——路由网络基础——路由转发

六、路由转发 1、最长匹配原则 最长匹配原则 是支持IP路由的设备默认的路由查找方式(事实上几乎所有支持IP路由的设备都是这种查找方式)。当路由器收到一个IP数据包时&#xff0c;会将数据包的目的IP地址与自己本地路由表中的表项进行逐位(Bit-By-Bit)的逐位查找&#xff0c;…

一元线性回归模型 多元线性回归模型回归模型评估

本人详解 作者:王文峰,参加过 CSDN 2020年度博客之星,《Java王大师王天师》 公众号:JAVA开发王大师,专注于天道酬勤的 Java 开发问题中国国学、传统文化和代码爱好者的程序人生,期待你的关注和支持!本人外号:神秘小峯 山峯 转载说明:务必注明来源(注明:作者:王文峰…