Go1.22 新特性:Slices 变更 Concat、Delete、Insert 等函数,对开发挺有帮助!

简介: Go1.22 新特性:Slices 变更 Concat、Delete、Insert 等函数,对开发挺有帮助!

在 Go1.22 这个新版本起,切片(Slices)新增和变更了一些行为。对于开发者相对更友好了一点。


以下涉及 Concat、Delete、DeleteFunc、Replace、Compact、CompactFunc、Insert 等函数的新版本调整的讲解和分享。


新增 Concat 函数


在以前的 Go 版本中,有一个很常见的使用场景,如果我们想要拼接两个切片。必须要手写类似如下的代码:


func main() {
 s1 := []string{"煎鱼", "咸鱼", "摸鱼"}
 s2 := []string{"炸鱼", "水鱼", "煎鱼"}
 s3 := append(s1, s2...)
 fmt.Println(s3)
}


输出结果:


[煎鱼 咸鱼 摸鱼 炸鱼 水鱼 煎鱼]


如果在 Go 工程中常用到,大家还会在类似 util 包上补一个这种函数,便于复用。搞不好还要基于不同的数据类型都实现一遍。

可能的实现如下:


func concatSlice[T any](first []T, second []T) []T {
 n := len(first)
 return append(first[:n:n], second...)
}
func main() {
 s1 := []string{"煎鱼", "炸鱼"}
 s2 := []string{"水鱼", "摸鱼", "煎鱼"}
 s3 := concatSlice(s1, s2)
 fmt.Println(s3)
}


输出结果:


[煎鱼 炸鱼 水鱼 摸鱼 煎鱼]


如果要合并超过 2 个的切片,这个函数的实现就更复杂一些。


但是!


在 Go1.22 起,新增了 Concat 函数,可以用于拼接(连接)多个切片。不需要自己维护一个公共方法了。


Concat 函数签名如下:


func Concat[S ~[]E, E any](slices ...S) S


使用案例如下:


import (
 "fmt"
 "slices"
)
func main() {
 s1 := []string{"煎鱼"}
 s2 := []string{"炸鱼", "青鱼", "咸鱼"}
 s3 := []string{"福寿鱼", "煎鱼"}
 resp := slices.Concat(s1, s2, s3)
 fmt.Println(resp)
}


该函数是基于泛型实现的,不需要像以前一样,每个类型都在内部实现一遍。用户使用起来非常方便。但需要确保传入的切片类型都是一致的。

其内部函数实现也比较简单。如下代码:


// Concat returns a new slice concatenating the passed in slices.
func Concat[S ~[]E, E any](slices ...S) S {
 size := 0
 for _, s := range slices {
  size += len(s)
  if size < 0 {
   panic("len out of range")
  }
 }
 newslice := Grow[S](nil, size)
 for _, s := range slices {
  newslice = append(newslice, s...)
 }
 return newslice
}


需要注意的是:当 size < 0 时会触发 panic。但我感觉这更多只是一个防御性的编程处理。一般情况下不会被触发。


变更 Delete 等函数行为结果


Go1.22 起,将对会缩小切片片段/大小的相关函数的结果行为进行调整,切片经过缩小后新长度和旧长度之间的元素将会归为零值


将会涉及如下函数:Delete、DeleteFunc、Replace、Compact、CompactFunc 等函数。


以下是一些具体的案例。分为旧版本(Go1.21)、新版本(Go1.22 及以后)。


Delete 相关函数


旧版本:


func main() {
 s1 := []int{11, 12, 13, 14}
 s2 := slices.Delete(s1, 1, 3)
 fmt.Println("s1:", s1)
 fmt.Println("s2:", s2)
}


输出结果:


s1: [11 14 13 14]
s2: [11 14]


新版本程序不变,运行结果发生了改变,输出结果为:


s1: [11 14 0 0]
s2: [11 14]


Compact 函数


旧版本:


func main() {
 s1 := []int{11, 12, 12, 12, 15}
 s2 := slices.Compact(s1)
 fmt.Println("s1:", s1)
 fmt.Println("s2:", s2)
}


输出结果:


s1: [11 12 15 12 15]
s2: [11 12 15]


新版本程序不变,运行结果发生了改变,输出结果为:


s1: [11 12 15 0 0]
s2: [11 12 15]


Replace 函数


旧版本:


func main() {
 s1 := []int{11, 12, 13, 14}
 s2 := slices.Replace(s1, 1, 3, 99)
 fmt.Println("s1:", s1)
 fmt.Println("s2:", s2)
}


输出结果:


s1: [11 99 14 14]
s2: [11 99 14]


新版本程序不变,运行结果发生了改变,输出结果为:


s1: [11 99 14 0]
s2: [11 99 14]


变更 Insert 函数行为,可能会 panic


旧版本:


func main() {
 s1 := []string{"煎鱼", "炸鱼", "水鱼"}
 s2 := slices.Insert(s1, 4)
 fmt.Println("s1:", s1)
 fmt.Println("s2:", s2)
}


输出结果:


s1: [煎鱼 炸鱼 水鱼]
s2: [煎鱼 炸鱼 水鱼]


新版本程序不变,运行结果发生了改变,输出结果为:


panic: runtime error: slice bounds out of range [4:3]
goroutine 1 [running]:
slices.Insert[...]({0xc00010e0c0?, 0x10100000010?, 0x7ecd5be280a8?}, 0xc00010aee8?, {0x0?, 0x60?, 0x10052e4c0?})
 ...


以上场景是使用 slices.Insert 函数下,以前没有填入具体要插入的元素,是会正常运行的。在新版本起,会直接导致 panic。

当然,如果一开始就有填入。无论是新老版本,都会导致 panic。相当于是修复了一个边界值了。


一点质疑


可能会有同学说,这不对劲啊。Go1 不是有兼容性保障吗?这么多函数的行为变更,是可以这么干的吗?


7c485763a224413d14c7fbaffef567f8.png


从官方文档角度来看是可以的,因为其强调了其当前文档并未承诺将过时元素归零或不归零。也就是没有承诺不变,但承诺过可能产生变更。


总结


今天我们针对切片(Slices)的各函数变更和新增进行了实际的讲解和案例分享。整体来看,还是基于泛型做的修修补补,虽然不是大功能特性。但是对于我们实际做 Go 工程开发的同学来讲,这是比较实在的。

相关文章
|
2月前
|
算法 Java Go
【GoGin】(1)上手Go Gin 基于Go语言开发的Web框架,本文介绍了各种路由的配置信息;包含各场景下请求参数的基本传入接收
gin 框架中采用的路优酷是基于httprouter做的是一个高性能的 HTTP 请求路由器,适用于 Go 语言。它的设计目标是提供高效的路由匹配和低内存占用,特别适合需要高性能和简单路由的应用场景。
250 4
|
4月前
|
数据采集 数据挖掘 测试技术
Go与Python爬虫实战对比:从开发效率到性能瓶颈的深度解析
本文对比了Python与Go在爬虫开发中的特点。Python凭借Scrapy等框架在开发效率和易用性上占优,适合快速开发与中小型项目;而Go凭借高并发和高性能优势,适用于大规模、长期运行的爬虫服务。文章通过代码示例和性能测试,分析了两者在并发能力、错误处理、部署维护等方面的差异,并探讨了未来融合发展的趋势。
352 0
|
2月前
|
JavaScript 前端开发 Java
【GoWails】Go做桌面应用开发?本篇文章带你上手Wails框架!一步步带你玩明白前后端双端的数据绑定!
wails是一个可以让你使用Go和Web技术编写桌面应用的项目 可以将它看作Go的快并且轻量级的Electron替代品。可以使用Go的功能,并结合现代化UI完成桌面应用程序的开发
491 4
|
2月前
|
存储 安全 Java
【Golang】(4)Go里面的指针如何?函数与方法怎么不一样?带你了解Go不同于其他高级语言的语法
结构体可以存储一组不同类型的数据,是一种符合类型。Go抛弃了类与继承,同时也抛弃了构造方法,刻意弱化了面向对象的功能,Go并非是一个传统OOP的语言,但是Go依旧有着OOP的影子,通过结构体和方法也可以模拟出一个类。
189 1
|
3月前
|
存储 Java Go
对比Java学习Go——函数、集合和OOP
Go语言的函数支持声明与调用,具备多返回值、命名返回值等特性,结合`func`关键字与类型后置语法,使函数定义简洁直观。函数可作为一等公民传递、赋值或作为参数,支持匿名函数与闭包。Go通过组合与接口实现面向对象编程,结构体定义数据,方法定义行为,接口实现多态,体现了Go语言的简洁与高效设计。
|
6月前
|
JSON 中间件 Go
Go 网络编程:HTTP服务与客户端开发
Go 语言的 `net/http` 包功能强大,可快速构建高并发 HTTP 服务。本文从创建简单 HTTP 服务入手,逐步讲解请求与响应对象、URL 参数处理、自定义路由、JSON 接口、静态文件服务、中间件编写及 HTTPS 配置等内容。通过示例代码展示如何使用 `http.HandleFunc`、`http.ServeMux`、`http.Client` 等工具实现常见功能,帮助开发者掌握构建高效 Web 应用的核心技能。
365 61
|
6月前
|
开发框架 安全 前端开发
Go Web开发框架实践:模板渲染与静态资源服务
Gin 是一个功能强大的 Go Web 框架,不仅适用于构建 API 服务,还支持 HTML 模板渲染和静态资源托管。它可以帮助开发者快速搭建中小型网站,并提供灵活的模板语法、自定义函数、静态文件映射等功能,同时兼容 Go 的 html/template 引擎,具备高效且安全的页面渲染能力。
|
6月前
|
人工智能 Dart Go
Go语言中的make和new函数的区别及使用场景
本文详细解析了Go语言中`make`和`new`函数的使用方法及区别。`make`用于创建切片、映射和通道等引用类型,返回初始化后的值;`new`用于创建任意类型的零值对象,返回指向该对象的指针。文章通过多个示例说明两者的应用场景,并总结了面试中可能遇到的相关问题,如底层实现、使用场景及优缺点等,帮助读者更好地理解和区分这两个函数。
194 1
|
7月前
|
Go 调度
GO语言函数的内部运行机制分析
以上就是Go语言中函数的内部运行机制的概述,展示了函数在Go语言编程中如何发挥作用,以及Go如何使用简洁高效的设计,使得代码更简单,更有逻辑性,更易于理解和维护。尽管这些内容深入了一些底层的概念,但我希望通过这种方式,将这些理论知识更生动、更形象地带给你,让你在理解的同时找到编程的乐趣。
142 5
|
7月前
|
Go Python
函数的定义与调用 -《Go语言实战指南》
本文介绍了 Go 语言中函数的核心特性与用法,包括基本定义格式、调用方式、多返回值、返回值命名、参数类型简写、可变参数、高阶函数及匿名函数等内容。通过示例代码详细展示了如何定义和使用不同类型的函数,使读者能够全面了解 Go 函数的灵活性与强大功能。
137 12

热门文章

最新文章