在 Go 中如何让结构体不可比较?

news2025/1/22 9:07:31

最近我在使用 Go 官方出品的结构化日志包 slog 时,看到 slog.Value 源码中有一个比较好玩的小 Tips,可以限制两个结构体之间的相等性比较,本文就来跟大家分享下。

在 Go 中结构体可以比较吗?

在 Go 中结构体可以比较吗?这其实是我曾经面试过的一个问题,我们来做一个实验:

定义如下结构体:

type Normal struct {
    a string
    B int
}

使用这个结构体分别声明 3 个变量 n1n2n3,然后进行比较:

n1 := Normal{
    a: "a",
    B: 10,
}
n2 := Normal{
    a: "a",
    B: 10,
}
n3 := Normal{
    a: "b",
    B: 20,
}

fmt.Println(n1 == n2)
fmt.Println(n1 == n3)

执行示例代码,输出结果如下:

$ go run main.go
true
false

可见 Normal 结构体是可以比较的。

如何让结构体不可比较?

那么所有结构体都可以比较吗?显然不是,如果都可以比较,那么 reflect.DeepEqual() 就没有存在的必要了。

定义如下结构体:

type NoCompare struct {
    a string
    B map[string]int
}

使用这个结构体分别声明 2 个变量 n1n2,然后进行比较:

n1 := NoCompare{
    a: "a",
    B: map[string]int{
        "a": 10,
    },
}
n2 := NoCompare{
    a: "a",
    B: map[string]int{
        "a": 10,
    },
}

fmt.Println(n1 == n2)

执行示例代码,输出结果如下:

$ go run main.go
./main.go:59:15: invalid operation: n1 == n2 (struct containing map[string]int cannot be compared)

这里程序直接报错了,并提示结构体包含了 map[string]int 类型字段,不可比较。

所以小结一下:

结构体是否可以比较,不取决于字段是否可导出,而是取决于其是否包含不可比较字段。

如果全部字段都是可比较的,那么这个结构体就是可比较的。

如果其中有一个字段不可比较,那么这个结构体就是不可比较的。

不过虽然我们不可以使用 == 对 n1n2 进行比较,但我们可以使用 reflect.DeepEqual() 对二者进行比较:

fmt.Println(reflect.DeepEqual(n1, n2))

执行示例代码,输出结果如下:

$ go run main.go
true

更优雅的做法

最近我在使用 Go 官方出品的结构化日志包 slog 时,看到 slog.Value 源码:

// A Value can represent any Go value, but unlike type any,
// it can represent most small values without an allocation.
// The zero Value corresponds to nil.
type Value struct {
    _ [0]func() // disallow ==
    // num holds the value for Kinds Int64, Uint64, Float64, Bool and Duration,
    // the string length for KindString, and nanoseconds since the epoch for KindTime.
    num uint64
    // If any is of type Kind, then the value is in num as described above.
    // If any is of type *time.Location, then the Kind is Time and time.Time value
    // can be constructed from the Unix nanos in num and the location (monotonic time
    // is not preserved).
    // If any is of type stringptr, then the Kind is String and the string value
    // consists of the length in num and the pointer in any.
    // Otherwise, the Kind is Any and any is the value.
    // (This implies that Attrs cannot store values of type Kind, *time.Location
    // or stringptr.)
    any any
}

可以发现,这里有一个匿名字段 _ [0]func(),并且注释写着 // disallow ==

_ [0]func() 的目的显然是为了禁止比较。

我们来实验一下,_ [0]func() 是否能够实现禁止结构体相等性比较:

v1 := Value{
    num: 1,
    any: 2,
}
v2 := Value{
    num: 1,
    any: 2,
}

fmt.Println(v1 == v2)

执行示例代码,输出结果如下:

$ go run main.go
./main.go:109:15: invalid operation: v1 == v2 (struct containing [0]func() cannot be compared)

可以发现,的确有效。因为 func() 是一个函数,而函数在 Go 中是不可比较的。

既然使用 map[string]int 和 _ [0]func() 都能实现禁止结构体相等性比较,那么我为什么说 _ [0]func() 是更优雅的做法呢?

_ [0]func() 有着比其他实现方式更优的特点:

它不占内存空间!

使用匿名字段 _ 语义也更强。

而且,我们直接去 Go 源码里搜索,能够发现其实 Go 本身也在多处使用了这种用法:

image.png

所以推荐使用 _ [0]func() 来实现禁用结构体相等性比较。

不过值得注意的是:当使用 _ [0]func() 时,不要把它放在结构体最后一个字段,推荐放在第一个字段。这与结构体内存对齐有关,我在《Go 中空结构体惯用法,我帮你总结全了!》 一文中也有提及。

NOTE: 对于  _ [0]func() 不占用内存空间的验证,就交给你自己去实验了。
提示:可以使用  fmt.Println(unsafe.Sizeof(v1), unsafe.Sizeof(v2)) 分别打印结构体  Value 的两个实例  v1v2 的内存大小。你可以删掉  _ [0]func() 字段再试一试。

总结

好了,在 Go 中如何让结构体不可比较这个小 Tips 就分享给大家了,还是比较有意思的。

作为开发者,我们需要保持好奇心和学习热情,不断探索新的技术,只有这样,我们才能在这个快速发展的时代中立于不败之地。介绍一款程序员都应该知道的软件JNPF快速开发平台,很多人都尝试用过它,它是功能的集大成者,任何信息化系统都可以基于它开发出来。

JNPF可以实现应用从创建、配置、开发、测试到发布、运维、升级等完整生命周期的管理。减少了传统应用程序的代码编写量,通过图形化、可视化的界面,以拖放组件的方式,即可快速生成应用程序的产品,大幅降低了开发企业管理类软件的难度。

当然,我更建议大家成为一个全栈,不要把自己的定位局限于前端。

感谢阅读本文

如果有什么建议,请在评论中让我知道。我很乐意改进。

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

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

相关文章

Elasticsearch docker 安装及基本用法

创建网络 首先通过命令创建一个网络 docker network create es-net然后查看网络 [rootDocker ~]# docker network ls NETWORK ID NAME DRIVER SCOPE 4e315f5e3ae7 bridge bridge local a501a9f3b4ee es-net bridge local ebca66b02e8c host …

十一、数据结构(图的最短路)

文章目录 基础部分最短路径问题用 D F S DFS DFS搜索所有的路径用 B F S BFS BFS求最短路径 最短路算法 F l o y d Floyd Floydcode(Floyd的实现): S P F A SPFA SPFAcode(基于邻接表的 S P F A ) SPFA) SPFA) D i j k s t r a Dijkstra Dijkstracode(dijkstra的实现…

PHP实现企业微信素材上传与获取的完整指南与踩坑日记

企业微信作为一款专门为企业打造的即时通讯工具,提供了丰富的功能和接口,其中包括素材管理。素材管理在企业内部的沟通、分享和展示中起着重要的作用。本篇文章将介绍如何使用PHP语言对接企业微信素材上传和获取的功能。 ## 1. 准备工作 首先&#xff0…

AI数据分析:Excel表格智能判断数据起点来计算增长率

工作任务:计算Excel表格中2023年1月到2024年4月的总增长率和复合增长率。 如果数据都有的情况下,公式很简单: 总增长率 (O2-B2)/B2 复合增长率 POWER((O2/B2),1/13)-1 但是,2023年1月、2月、3月的数据,有些有&…

LLM2Vec论文阅读笔记

这是篇LLM论文,用decoder-like的LLM去提取embedding文章认为,decoder-like的LLM在text embedding task表现不优的一大原因就是其casual attention mechanism,其实就是mask的问题。所以只要对现有的decoder-only LLM进行如下三步改进&#xff…

SpringBoot配置第三方专业缓存技术jetcache方法缓存方案

jetcache方法缓存 我们可以给每个方法配置缓存方案 JetCache 是一个基于 Java 的缓存库,支持多种缓存方案和缓存策略,主要用于提升应用程序的性能和响应速度。它提供了多种缓存模式和特性,可以根据需求选择合适的缓存方案。 JetCache 的主…

VPC Access Connector 介绍 - 让 Non-VPC product 也可以访问VPC Network内的资源

什么是VPC product 和 非 VPC product 在GCP 上, VPC product 指的是属于某个制定的vpc subnet, 具有至少1个 该 subnet 的内网ip的产品 常见的例如: compute engine / MIG (managed instances group)某些dataflow job (指定了 可选参数subnet )Cloud …

C++设计模式——Composite组合模式

一,组合模式简介 真实世界中,像企业组织、文档、图形软件界面等案例,它们在结构上都是分层次的。将系统分层次的方式使得统一管理和添加不同子模块变得容易,在软件开发中,组合模式的设计思想和它们类似。 组合模式是…

复分析——第4章——Fourier变换(E.M. Stein R. Shakarchi)

第4章 Fouier变换 Raymond Edward Alan Christopher Paley, Fellow of Trinity College, Cambridge, and International Research Fellow at the Massachusetts Institute of Technology and at Harvard University, was killed by an avalanche on April 7, 1933, whi…

Golang | Leetcode Golang题解之第166题分数到小数

题目&#xff1a; 题解&#xff1a; func fractionToDecimal(numerator, denominator int) string {if numerator%denominator 0 {return strconv.Itoa(numerator / denominator)}s : []byte{}if numerator < 0 ! (denominator < 0) {s append(s, -)}// 整数部分numer…

中科数安 | 加密管理系统

中科数安提供的加密管理系统是一套全面而高效的数据安全解决方案&#xff0c;旨在保护企业核心文件资料的安全。该系统结合了多种先进的技术手段和管理策略&#xff0c;确保企业数据在存储、传输和使用过程中都得到严格的保护。 www.weaem.com 以下是中科数安加密管理系统的主要…

ES 8.14 Java 代码调用,增加knnSearch 和 混合检索 mixSearch

1、pom依赖 <dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-client</artifactId><version>8.14.0</version></dependency><dependency><groupId>co.elastic.clients<…

可解释机器学习之SHAP方法

以Breast cancer wisconsin (diagnostic) dataset数据集为例。 # Built-in libraries import math import numpy as np import pandas as pd# Visualization libraries import matplotlib.pyplot as plt import seaborn as sns# Sklearn libraries # from skle…

项目估算

1.项目估算的基本内容 2.基本估算方法 3.WBS估算法 4.资源估算的基本过程 5.由工作量和开发周期来估算 6.资源特征描述 7.项目角色职能确定 8.工期估算方法 9.成本估算方法 10.LOC估算法 LOC&#xff08;Lines of Code&#xff0c;代码行数&#xff09;估算法是一种简单且直接…

Gracia:打造超逼真VR体验,引领体积视频新时代

在数字化浪潮中,虚拟现实(VR)技术以其独特的沉浸式体验,逐渐成为科技前沿的热点。而在这个领域中,Gracia正以其创新的体积视频技术,为用户带来前所未有的真实感VR体验,致力于成为“空间计算领域的YouTube”。 Gracia,一个充满活力的初创公司,已经获得了120万美元的种…

【记录44】【案例】echarts地图

效果&#xff1a;直接上效果图 环境&#xff1a;vue、echarts4.1.0 源码 // 创建容器 <template><div id"center"></div> </template>//设置容器大小&#xff0c;#center { width: 100%; height: 60vh; }这里需注意&#xff1a;笔者在echar…

音频基础知识和音频指标

音频基础知识 声音 声音&#xff08;sound)是由物体振动产生的声波。物体在一秒钟之内振动的次数叫做频率&#xff0c;单位是赫兹&#xff0c;字母Hz。人耳可以识别的声音频率在 20 Hz~20000 Hz之间&#xff1b; 声音三要素&#xff1a; 响度 响度&#xff0c;…

谷歌Google广告开户是怎么收费的?

谷歌Google广告无疑是企业拓展全球视野、精准触达目标客户的强大引擎。而作为这一旅程的启航站&#xff0c;开户流程的便捷性与成本效益成为了众多企业关注的焦点。云衔科技&#xff0c;作为数字化营销解决方案与SaaS软件服务的领军者&#xff0c;正以其专业、高效的服务体系&a…

【凤凰房产-注册安全分析报告-缺少轨迹的滑动条】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 1. 暴力破解密码&#xff0c;造成用户信息泄露 2. 短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉 3. 带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造…

Eureka 学习笔记(1)

一 、contextInitialized() eureka-core里面&#xff0c;监听器的执行初始化的方法&#xff0c;是contextInitialized()方法&#xff0c;这个方法就是整个eureka-server启动初始化的一个入口。 Overridepublic void contextInitialized(ServletContextEvent event) {try {init…