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,一经查实,立即删除!

相关文章

在 Oracle Linux 8.9 上安装中文和日文字体的完整指南

在 Oracle Linux 8.9 上安装中文和日文字体的完整指南 在 Oracle Linux 8.9 上安装中文和日文字体的完整指南前提条件安装步骤1. 更新系统2. 安装字体包安装中文字体安装日文字体 3. 安装字体配置工具4. 更新字体缓存5. 验证安装 可能遇到的问题及解决方案结语 在 Oracle Linux…

(一)SvelteKit教程:hello world

(一)SvelteKit教程:hello world sveltekit 的官方教程,在这里:Creating a project • Docs • SvelteKitCreating a project • Docs • SvelteKit 我们可以按照如下的步骤来创建一个项目: npm create s…

CentOs7 安装单机版redis

1.安装依赖 redis是由C语言开发,因此安装之前必须要确保服务器已经安装了gcc,可以通过如下命令查看机器是否安装: gcc -v如果没有安装则通过以下命令安装: yum install -y gcc如果安装gcc依赖报错则执行yum升级命令 # 先执行升…

NSIS 入门教程 (三)

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

Shell编辑之条件语句

一,条件测试操作 1:文件测试 文件测试操作用来检查文件的各种属性,如文件是否存在、是否可读、是否为空等。常用的文件测试操作符包括: -e 文件存在性测试-f 是否为普通文件-d 是否为目录-r 是否可读-w 是否可写-x 是否可执行-s…

学懂C#编程:常用高级技术——委托(Delegate)的概念及详细使用讲解

目录 委托的概念 常用应用场景 优势 C#中的委托(Delegate)是一种引用类型,它允许你封装一个方法的引用。委托类似于函数指针,但提供了更强大和类型安全的功能。委托在C#中扮演着多重角色,常用于实现回调方法、事件…

【栈和队列】

目录 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;其中最高位为符号位…

关于Mysql 的on duplicate key update操作,导致主键不连续自增的问题

一 相关说明 在实际的开发中,经常会遇到这样的场景:若数据库里面不存在数据,则插入;若存在,则更新。在Mysql中,可以使用ON DUPLICATE KEY UPDATE,一步就能完成上述操作。简单说,就是数据库中存在某个记录时,执行这个语句会更新,而不存在这条记录时,就会插入。 需要说…

CesiumJS【Basic】- #009 切换地形数据源

文章目录 切换地形数据源1 目标2 实现切换地形数据源 1 目标 切换地形数据源 2 实现 这段代码定义了一个名为 toggleTerrainProvider 的方法,用于在 CesiumJS 中切换地形数据源。如果当前的地形数据源是 EllipsoidTerrainProvider(一个简单的椭球体地形),则将其切换为 …

黑龙江等保测评

黑龙江等保测评概述 黑龙江等保测评是指根据《中华人民共和国网络安全法》及《信息安全等级保护管理办法》等相关法律法规&#xff0c;对信息系统安全保护能力进行评估和验证的过程。它旨在确保重要信息系统能够达到相应的安全保护级别&#xff0c;有效防范各种安全威胁&#…

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

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

【JavaScript脚本宇宙】提升用户体验:比较六种领先的图像灯箱库及其独特功能

为您的网站选择完美的图像灯箱&#xff1a;六种流行选项的全面比较 前言 图像灯箱库在网页设计中起着至关重要的作用&#xff0c;它们提供了一种优雅的方式来展示图片、视频和其他多媒体内容。本文将比较六种常见的图像灯箱库&#xff1a;Lightbox2、Fancybox、Magnific Popu…

算法训练与程序竞赛题目集合(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;其他用户对探店笔记的…