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

news2025/2/28 11:13:36

背景

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

func returnError() error {
    var err error
    defer 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() string
    Color2() string
}

type BigBox struct {
    ColorStr string
    Volume   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 = c
    return b
}

func (b *BigBox) SetColor2(c string) {
    b.ColorStr = c
}

func main() {
    box := BigBox{}
    boxCopy := box.SetColor("red")
    var box2 Box = box
    fmt.Printf("after SetColor return box color: %v\n", box2.Color())
    fmt.Printf("after SetColor return boxCopy color: %v\n", boxCopy.Color())
    var box3 Box = &box
    box.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 error
    defer 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 error
    defer 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.coloradmin.cn/o/1853778.html

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

相关文章

PMBOK® 第六版 管理项目知识

目录 读后感—PMBOK第六版 目录 在前面的文章中,输入环节都可以看见有事业环境因素、组织过程资产;工具与技术都有专家判断。都是说明知识的重要性。 虽然项目具有其独特的、唯一性,但项目相关的经验却能如同家族传承般,被持续地…

【Python】已解决:安装python-Levenshtein包时遇到的subprocess-exited-with-error问题

文章目录 一、分析问题背景二、可能出错的原因三、错误代码示例四、正确代码示例及解决方案五、注意事项 已解决:安装python-Levenshtein包时遇到的subprocess-exited-with-error问题 一、分析问题背景 在安装python-Levenshtein这个Python包时,有时会…

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

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

196.每日一题:检测大写字母(力扣)

代码解决 class Solution { public:bool detectCapitalUse(string word) {int capitalCount 0;int n word.size();// 统计大写字母的数量for (char c : word) {if (isupper(c)) {capitalCount;}}// 检查是否满足三种情况之一if (capitalCount n) {// 全部字母都是大写return…

[最全]设计模式实战(一)UML六大原则

UML类图 UML类图是学习设计模式的基础,学习设计模式,主要关注六种关系。即:继承、实现、组合、聚合、依赖和关联。 UML类图基本用法 继承关系用空心三角形+实线来表示。实现接口用空心三角形+虚线来表示。eg:大雁是最能飞的,它实现了飞翔接口。 关联关系用实线箭头来表示…

Python武器库开发-武器库篇之ThinkPHP 5.0.23-RCE 漏洞复现(六十四)

Python武器库开发-武器库篇之ThinkPHP 5.0.23-RCE 漏洞复现(六十四) 漏洞环境搭建 这里我们使用Kali虚拟机安装docker并搭建vulhub靶场来进行ThinkPHP漏洞环境的安装,我们进入 ThinkPHP漏洞环境,可以 cd ThinkPHP,然…

c#使用自带库对字符串进行AES加密、解密

文章目录 1 代码1.1 定义Aes加密类块1.2 在主函数中调用 2 获取Key和IV2.1 基本方法2.2 自定义Key2.3 技术方面的原理 参考文章: C#软件加密实例? 。 参考官文: Aes 类。 在使用C#的自带的System.Security.Cryptography.Aes模块进行加密和解…

mediasoup 源码分析 (八)分析PlainTransport

mediasoup 源码分析 (六)分析PlainTransport 一、接收裸RTP流二、mediasoup 中udp建立过程 tips 一、接收裸RTP流 PlainTransport 可以接收裸RTP流,也可以接收AES加密的RTP流。源码中提供了一个通过ffmpeg发送裸RTP流到mediasoup的脚本&…

基于PyTorch设计的全景图合成系统【文末完整工程源码下载】

前言 本项目实现基于PyTorch将多张图片合成为一张全景图。(图像存储路径为/images/1)。 作者:阿齐Archie(联系我微信公众号:阿齐Archie) 使用的图片为: 合成后为: 这个全景图项目主…

eNSP启动设备失败,错误代码40,网卡配置正常,虚拟机导致的错误解决过程

安装eNSP后出现以下错误。 按照帮助文档,查看了相关软件,尤其是vitualbox的版本以及网卡问题。网卡设置正常,vitualbox也匹配成功。 附:vitualbox各个版本的下载地址: 关于网卡名称的修改方法,参照博客 …

python实现技术指标(简单移动平均,加权移动平均线,指数移动平均线)

移动平均线是最常见的技术指标,它能够去除时间序列的短期波动,使得数据变得平滑,从而可以方便看出序列的趋势特征。常见的移动平均线有简单移动平均线,加权移动平均线,指数移动平均线。 一. 简单移动平均(SMA) 简单移…

2.超声波测距模块

1.简介 2.超声波的时序图 3.基于51单片机实现的代码 #include "reg52.h" #include "intrins.h" sbit led1P3^7;//小于10,led1亮,led2灭 sbit led2P3^6;//否则,led1灭,led2亮 sbit trigP1^5; sbit echo…

电容的命名规则

给如下参数给采购,就可以获取 还有一些参数需要重视 容值随着环境温度而保持的程度 常规应用时是可以不用看材质,但是如果使用在新能源汽车和极端环境下的电子产品,就需要关注材质,曾有供应商把可用级电容供应车企,导致…

动手学深度学习(Pytorch版)代码实践 -计算机视觉-36图像增广

6 图片增广 import matplotlib.pyplot as plt import numpy as np import torch import torchvision from d2l import torch as d2l from torch import nn from PIL import Image import liliPytorch as lp from torch.utils.data import Dataset, DataLoaderplt.figure(cat)…

8.DELL R730服务器对RAID5进行扩容

如果服务器的空间不足了,如何进行扩容?我基本上按照如何重新配置虚拟磁盘或添加其他硬盘来进行操作。我的机器上已经有三块硬盘了,组了Raid5,现在再添加一块硬盘。 先把要添加的硬盘插入服务器,无论是在IDRAC还是管理…

基于S7-200PLC的全自动洗衣机控制系统设计

wx供重浩:创享日记 那边对话框发送:plc洗衣 获取完整无水印设计说明报告(含程序梯形图) 1.自动洗衣机PLC控制的控制要求 1.1全自动洗衣机的基本结构、工作流程和工作原理 1.自动洗衣机的基本结构 2.自动洗衣机的工作流程 自动洗…

RepVGG论文阅读笔记

目录 RepVGG: Making VGG-style ConvNets Great Again摘要INTRODUCTION—简介RepVGG BlockModel Re-parameterization -- 模型重参数化融合Conv2d和BN,将三个分支上的卷积算子和BN算子都转化为卷积算子(包括卷积核和偏置)多分支融合&#xff…

【Python】已解决:pymssql引发的MSSQLDatabaseException错误

文章目录 一、分析问题背景二、可能出错的原因三、错误代码示例四、正确代码示例五、注意事项 已解决:pymssql引发的MSSQLDatabaseException错误 一、分析问题背景 在Python中使用pymssql库与Microsoft SQL Server数据库交互时,有时会遇到pymssql._mss…

k8s 部署 ruoyi 前后端分离项目

本文视频版 https://www.bilibili.com/video/BV17ugkePEeN 参考 https://blog.csdn.net/qq_50247813/article/details/136934090 https://gitee.com/nasaa/RuoYi-Vue-cloud https://www.itsgeekhead.com/tuts/kubernetes-129-ubuntu-22-04-3/ https://kubernetes.io/docs/se…

安装react之nvm版本低引起的问题

1.背景 准备搭建一个react,然后看官网文档 创建项目,使用命令行 npx create-next-applatest 创建项目的流程都是正常的。当我准备运行项目的时候,报错了 原先的报错没有了,从网上找了一个类似的 重要的内容是:当前…