切片上的健壮范型函数

在这篇博客文章中,我们将讨论如何通过了解切片在内存中的表示方式以及这对垃圾收集器的影响,更有效地使用slices包中提供的函数。我们还将介绍我们最近如何调整这些函数,使它们变得不那么令人惊讶。

借助类型参数,我们可以为所有类型的切片编写像slices.Index这样的函数:

// Index 返回s中v首次出现的索引,
// 如果不存在,则返回-1。
func Index[S ~[]E, E comparable](s S, v E) int {for i := range s {if v == s[i] {return i}}return -1
}

不再需要为每种不同类型的元素再次实现Index

slices包包含许多这样的助手函数,用于对切片执行常见操作:

    s := []string{"Bat", "Fox", "Owl", "Fox"}s2 := slices.Clone(s)slices.Sort(s2)fmt.Println(s2) // [Bat Fox Fox Owl]s2 = slices.Compact(s2)fmt.Println(s2)                  // [Bat Fox Owl]fmt.Println(slices.Equal(s, s2)) // false

一些新函数(InsertReplaceDelete等)修改切片。要了解它们是如何工作的,以及如何正确使用它们,我们需要检查切片的底层结构。

切片是数组一部分的视图。在内部,切片包含一个指针、一个长度和一个容量。两个切片可以有相同的底层数组,并且可以查看重叠的部分。

例如,这个切片s是对大小为6的数组中4个元素的视图:

在这里插入图片描述

如果函数更改了作为参数传递的切片的长度,则需要将新的切片返回给调用者。如果不需要增长,底层数组可能保持不变。这解释了为什么append和slices.Compact返回一个值,但slices.Sort,仅重新排序元素,不返回值。

考虑删除切片一部分的任务。在泛型出现之前,从切片s中删除部分s[2:5]的标准方法是调用append函数将结尾部分复制到中间部分:

s = append(s[:2], s[5:]...)

语法复杂且容易出错,涉及到子切片和可变参数。我们添加了slice.Delete来简化元素的删除:

func Delete[S ~[]E, E any](s S, i, j int) S {return append(s[:i], s[j:]...)
}

一行函数Delete更清晰地表达了程序员的意图。考虑长度为6、容量为8的切片s,包含指针:

在这里插入图片描述

这个调用从切片s中删除了s[2]s[3]s[4]的元素:

s = slices.Delete(s, 2, 5)

在这里插入图片描述

通过向左移动元素s[5]来填补索引2、3、4处的空隙,并将新的长度设置为3

Delete不需要分配新数组,因为它就地移动元素。像append一样,它返回一个新切片。slices包中的许多其他函数也遵循这种模式,包括CompactCompactFuncDeleteFuncGrowInsertReplace

调用这些函数时,我们必须认为原始切片无效,因为底层数组已经被修改。调用函数但忽略返回值将是一个错误:

    slices.Delete(s, 2, 5) // 不正确!// s的长度仍然相同,但内容被修改了

不希望的生存期问题

在Go 1.22之前,slices.Delete并没有修改新旧切片长度之间的元素。虽然返回的切片不包括这些元素,但在原始的、现在无效的切片的末尾创建的“空隙”继续保留它们。这些元素可能包含指向大对象(20MB图像)的指针,垃圾收集器不会释放与这些对象关联的内存。这导致内存泄漏,可能导致严重的性能问题。

在上述示例中,我们成功地从s[2:5]中删除了指针p2p3p4,通过将一个元素向左移动。但是p3p4仍然存在于底层数组中,超出s的新长度。垃圾收集器不会回收它们。更不明显的是,p5不是被删除的元素之一,但是由于数组灰色部分保留的p5指针,其内存可能仍然泄漏。

对于开发者来说,如果他们不知道“不可见”的元素仍在占用内存,这可能会令人困惑。

因此,我们有两个选择:

  • 保持Delete的高效实现。如果用户想确保指向的值可以被释放,让用户自己将过时的指针设置为nil
  • 或者更改Delete,始终将过时的元素设置为零。这是额外的工作,使得Delete稍微效率低一些。将指针置零(设置为nil)可以在它们变得不可达时启用对象的垃圾收集。

哪个选项最好并不明显。第一个默认提供性能,第二个默认提供内存节俭。

解决方案

一个关键观察是,“将过时的指针设置为nil”并不像看起来那么容易。事实上,这项任务是如此容易出错,以至于我们不应该让用户承担编写它的负担。出于实用主义,我们选择修改CompactCompactFuncDeleteDeleteFuncReplace五个函数的实现,以“清除尾部”。一个好的副作用是,认知负担减少了,用户现在不需要担心这些内存泄漏了。

在Go 1.22中,调用Delete后,内存看起来像这样:

在这里插入图片描述

五个函数中的代码改动使用了新的内置函数clear(Go 1.21)将过时元素设置为s元素类型的零值:

img

E是指针、切片、映射、通道或接口的类型时,E的零值是nil

测试失败

这一变化导致了一些在Go 1.21中通过的测试在Go 1.22中失败,当切片函数被不正确使用时。这是个好消息。当你有一个bug时,测试应该让你知道。

如果你忽略了Delete的返回值:

slices.Delete(s, 2, 3)  // !! 不正确 !!

那么你可能错误地假设s不包含任何nil指针。在Go Playground中的示例。

如果你忽略了Compact的返回值:

slices.Sort(s) // 正确
slices.Compact(s) // !! 不正确 !!

那么你可能错误地假设s已正确排序并压缩。示例。

如果你将Delete的返回值分配给另一个变量,并继续使用原始切片:

u := slices.Delete(s, 2, 3)  // !! 不正确,如果你继续使用s !!

那么你可能错误地假设s不包含任何nil指针。示例。

如果你意外地遮蔽了切片变量,并继续使用原始切片:

s := slices.Delete(s, 2, 3)  // !! 不正确,使用:=而不是= !!

那么你可能错误地假设s不包含任何nil指针。示例。

结论

slices包的API相比传统的预泛型语法来删除或插入元素有所改进。

我们鼓励开发者使用新函数,同时避免上述列出的“陷阱”。

得益于最近实现的变更,一类内存泄漏被自动避免,无需对API进行任何更改,也不需要开发者做额外工作。

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

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

相关文章

C++之职工管理系统

1、管理系统需求 职工管理系统可以用来管理公司内所有员工的信息 主要利用C来实现一个基于多态的职工管理系统 公司中职工分为三类:普通员工、经理、老板,显示信息时,需要显示职工编号、职工姓名、职工岗位、以及职责。 普通员工职责:完成经理交给的…

自己写的whoami

一、代码 #include<stdio.h> #include<stdlib.h> #include<proc/readproc.h> int main() {struct PROCTAB *pt;struct proc_t *p;char *cmd;ptmalloc(sizeof(struct PROCTAB));pmalloc(sizeof(struct proc_t));ptopenproc(0x0028);while(readproc(pt,p)!NUL…

手撸dynamic源码详细讲解

本文源码解析基于3.3.1版本。只截了重点代码&#xff0c;如果需要看完整代码&#xff0c;可以去github拉取。 1 自动配置的实现 一般情况下&#xff0c;一个starter的最好入手点就是自动配置类&#xff0c;在 META-INF/spring.factories文件中指定自动配置类入口 org.spring…

CentOS无法解析部分网站(域名)

我正在安装helm软件&#xff0c;参考官方文档&#xff0c;要求下载 get-helm-3 这个文件。 但是我执行该条命令后&#xff0c;报错 连接被拒绝&#xff1a; curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 # curl: (7) Fai…

使用 pg_profile 在 Postgres 中生成性能分析报告

前言&#xff1a; postgres数据库中拥有大量的辅助插件用于帮助DBA更好的分析数据库性能或整个集群&#xff0c;包括索引、I/O、CPU和内存等&#xff0c;pg_profile是基于PostgreSQL标准统计信息视图的诊断工具&#xff0c;它类似于Oracle AWR架构&#xff0c;和Oracle一样&am…

threejs简单创建一个几何体(一)

1.下包引入 //下包 npm install three yarn add three//引入 import * as THREE from three2.创建场景,摄像机 // 1.创建场景const scene new THREE.Scene()// 2.创建摄像机//第一个参数是视角,一般在60-90之间,第二个参数是场景的尺寸,一般取显示器的宽高,第三个参数是开始位…

下载chromedrive,使用自动化

1、先看一下自己浏览器的版本 2、访问 https://googlechromelabs.github.io/chrome-for-testing/

射影几何 -- 摄像机几何 1

三维计算机视觉的主要任务是利用三维物体的二维图像所包含的信息&#xff0c;获取三维物体的空间位置与形状等几何信息&#xff0c;并在此基础上识别三维物体。 摄像机关于空间平面的投影是平面到平面的一个二维中心投影变换 对于空间物体&#xff0c;由于摄像机将三维物体表面…

单例模式( Singleton)——创建型模式

单例模式——创建型模式 什么是单例模式&#xff1f; 单例模式是一种创建型设计模式&#xff0c; 让你能够保证一个类只有一个实例&#xff0c; 并提供一个访问该实例的全局节点。简单来说如果你创建了一个对象&#xff0c; 过一会儿后你决定再创建一个新对象&#xff0c; 此…

中国京津冀太阳能光伏推进大会暨展览会

能源是国民经济发展的重要基础之一。随着国民经济的发展&#xff0c;能源的缺口增大&#xff0c;能源安全及能源在国民经济中的地位越显突出。我国是世界上少数几个能源结构以煤为INVITATION主的国家之一&#xff0c;也是世界上最大的煤炭消费国&#xff0c;燃煤造成的环境污染…

Linux操作系统——常见指令(1)

今天分享一下Linux操作系统常见一些指令。今天介绍 ls pwd cd touch mkdir rmdir rm这几个指令。 ls指令 语法 ls 选项 目录或者文件 功能 对于目录&#xff0c;该命令列出该目录下的所有子目录和文件&#xff0c;对于文件&#xff0c;将列出文件名以及其他信息。 我们常用…

【单调栈】代码随想录算法训练营第六十天 |84.柱状图中最大的矩形(待补充)

84.柱状图中最大的矩形 1、题目链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 2、文章讲解&#xff1a;代码随想录 3、题目&#xff1a; 给定 n 个非负整数&#xff0c;用来表示柱状图中各个柱子的高度。每个柱子彼此相邻&#xff0c;且宽度为 1 。 求在该柱…

将SQL数据库转换为Mysql数据库

一、准备工作 1、SQL server安装包与已经有数据的mdf、ldf数据库文件&#xff1b; 2、.net Framework安装包&#xff1b;&#xff08;用于支持SQL Server安装的组件&#xff09; 3、MySql安装包&#xff1b;&#xff08;用于目标数据库的环境安装&#xff09; 4、navicat安装包…

基于SpringBoot的“家乡特色推荐系统”的设计与实现(源码+数据库+文档+PPT)

基于SpringBoot的“家乡特色推荐系统”的设计与实现&#xff08;源码数据库文档PPT) 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBoot 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 系统首页界面图 用户注册界面图 文章分享界面…

WeiPHP Notice/index接口处存在RCE漏洞

产品介绍 WeiPHP是一款基于PHP开发的开源微信公众号开发框架。它提供了丰富的功能和易于使用的接口&#xff0c;使开发者能够快速构建和管理微信公众号应用。WeiPHP支持自定义菜单、消息管理、用户管理、素材管理、支付接口等功能&#xff0c;同时还提供了插件机制和模块化开发…

【DL经典回顾】激活函数大汇总(八)(Maxout Softmin附代码和详细公式)

激活函数大汇总&#xff08;八&#xff09;&#xff08;Maxout & Softmin附代码和详细公式&#xff09; 更多激活函数见激活函数大汇总列表 一、引言 欢迎来到我们深入探索神经网络核心组成部分——激活函数的系列博客。在人工智能的世界里&#xff0c;激活函数扮演着不…

学生时期学习资源同步-1 第一学期结业考试题4

原创作者&#xff1a;田超凡&#xff08;程序员田宝宝&#xff09; 版权所有&#xff0c;引用请注明原作者&#xff0c;严禁复制转载

PBKDF2算法:保障密码安全的利器

title: PBKDF2算法&#xff1a;保障密码安全的利器 date: 2024/3/14 16:40:05 updated: 2024/3/14 16:40:05 tags: PBKDF2算法密码安全性迭代盐值密钥 PBKDF2算法起源&#xff1a; PBKDF2&#xff08;Password-Based Key Derivation Function 2&#xff09;算法是一种基于密码…

如何理解闭包

闭包是编程语言中一个重要的概念&#xff0c;特别是在函数式编程中常常会遇到。以下是对闭包的理解&#xff1a; 1. 定义&#xff1a; 闭包是一种函数&#xff0c;它引用了在其定义范围之外的自由变量&#xff08;非全局变量&#xff09;&#xff0c;并且这些引用的变量在函数…

pip 配置镜像加速安装

在使用pip安装Python第三方库时&#xff0c;默认是使用pip官网的非常慢&#xff0c;可通过配置国内镜像源加速下载速度&#xff0c;以下是如何使用国内镜像源安装Python库的两种常见方式&#xff1a; 临时使用镜像源安装 如果你只是想临时使用某个镜像源安装单个或几个库&…