专栏名称: GoCN
最具规模和生命力的 Go 开发者社区
目录
相关文章推荐
高分子科学前沿  ·  中山大学吴丁财、朱有龙团队《AM》:芳香胺连 ... ·  8 小时前  
高分子科学前沿  ·  天津大学胡文平团队,最新Nature ... ·  昨天  
高分子科学前沿  ·  把“磷”换成“硫”,发了一篇Science, ... ·  昨天  
高分子科学前沿  ·  颜宁,拟获国家级荣誉! ·  2 天前  
一念行者  ·  以大海而存在,让波浪境生境灭 ·  2 天前  
51好读  ›  专栏  ›  GoCN

Go 1.22 的新增功能系列之一:cmp.Or

GoCN  · 公众号  ·  · 2024-04-25 17:15

正文

截至撰写本文时,Go 1.22 已经发布几个月了。早就该结束我为 1.22 所做的工作的系列了。抱歉耽搁了这么久,我最近忙于生活事务。如果您错过了我关于reflect.TypeFor(https://blog.carlana.net/post/2024/golang-reflect-type-for/) 和slices.Concat(https://blog.carlana.net/post/2024/golang-slices-concat/) 的帖子,请务必关注这些帖子。

我为 Go 1.22 提出并实现的最终函数是 cmp.Or。在 Go Time 中,我将其称为“1.22 的隐藏宝石”。这是一个简单的函数,但有很多潜在的用途和令人惊讶的长背景故事。

首先我们看一下代码:

// Or returns the first of its arguments that is not equal to the zero value.// If no argument is non-zero, it returns the zero value.func Or[T comparable](vals ...T) T {  var zero T  for _, val := range vals {    if val != zero {      return val    }  }  return zero}

正如我对 Go 的贡献一样,它非常简短。它只是比较其参数并返回第一个不是 0 nil "" 或其类型的零值的参数。

你如何使用它?

cmp.Or 的主要用途是获取字符串并返回第一个非空白字符串。例如,在搜索公共开源 Go 存储库时,我在网上发现了很多代码,这些代码尝试获取环境变量,但如果环境变量为空则返回默认值。对于 cmp.Or ,这看起来像 cmp.Or(os.Getenv("SOME_VARIABLE"), "default")

它也适用于数字和指针。

以下是我的真实代码库中的一些实际用途:

body := cmp.Or(page.Body, rawContent)name := cmp.Or(jwt.Username(), "Almanack")credits = append(credits, cmp.Or(credit.Name, credit.Byline))metadata.InternalID = cmp.Or(    xhtml.InnerText(rows.Value("slug")),    xhtml.InnerText(rows.Value("internal id")),    metadata.InternalID,)scope.SetTag("username", cmp.Or(userinfo.Username(), "anonymous"))currentUl = cmp.Or(    xhtml.Closest(currentUl.Parent, xhtml.WithAtom(atom.Ul)),    currentUl,)

正如您所看到的,大多数用途只是查看字符串来提供后备值,但最后一个示例是寻找非零的 *html.Node


cmp.Or 的另一个主要用途是与 cmp.Compare 一起使用来创建多部分比较:

type Order struct {  Product  string  Customer string  Price    float64}orders := []Order{  {"foo", "alice", 1.00},  {"bar", "bob", 3.00},  {"baz", "carol", 4.00},  {"foo", "alice", 2.00},  {"bar", "carol", 1.00},  {"foo", "bob", 4.00},}// Sort by customer first, product second, and last by higher priceslices.SortFunc(orders, func(a, b Order) int {  return cmp.Or(    cmp.Compare(a.Customer, b.Customer),    cmp.Compare(a.Product, b.Product),    cmp.Compare(b.Price, a.Price),  )})
foo alice 2.00foo alice 1.00bar bob 3.00foo bob 4.00bar carol 1.00baz carol 4.00

请注意,由于 cmp.Or 无法进行短路评估,因此即使客户名称不同,也会比较每个商品的产品名称和价格,这使得这样做是多余的。

通往 cmp.Or 的路很长。早在 2016 年,Stephen Kampmann 就提出了 strings.First,但那是在现代 Go 提案系统之前,因此该提案实际上并未得到评估。2020 年,我提出了一个新的运算符 ?? ,其工作方式与 cmp.Or 类似,但具有短路功能。它可以像 port := os.Getenv("PORT") ?? DefaultPort 一样使用。Ian Lance Taylor 当时指出,如果 Go 拥有泛型,则可以将其实现(无需短路)作为泛型辅助函数。在打开 ?? 的问题后不久,我最终编写了一个 stringutils.First 辅助函数供我个人使用,并且我很快发现自己在代码中到处使用它。

当泛型最终在 2021 年添加到 Go 的 beta 版本中时,我编写了一个名为truthy 的包,它使用reflect.Value.IsZero() 来报告任何值是否为零,但我发现使用反射会导致分配(这最终得到了改进)比正常比较慢 50 倍(在 Go 1.22 中已改进为仅 25 倍)。在当前版本的 Go 中,仅使用带有 comparable 的泛型(如 cmp.Or )而不使用反射会造成大约 2 倍的损失。

2022 年,我提出了一项关于通用通用零值的提案,该提案作为现有讨论的重复而结束,但 Russ Cox 在 2023 年提出了基本相同的提案,并被接受。根据提案,内置常量







请到「今天看啥」查看全文