当前位置:首页 > 科技  > 软件

业内大佬怒喷:Go 正朝着错误的方向发展

来源: 责编: 时间:2024-06-27 07:58:00 63观看
导读大家好,国外知名开源大佬 Aliaksandr Valialkin[1],最近针对即将正式发布的 Go1.23 中的迭代器写了篇文章[2]怒喷。引起了巨大的社区热议。迭代器这一新特性,有认同也有否定。无论怎么说,Go 新的复杂度来了。今天分享他针

大家好,国外知名开源大佬 Aliaksandr Valialkin[1],最近针对即将正式发布的 Go1.23 中的迭代器写了篇文章[2]怒喷。引起了巨大的社区热议。SZE28资讯网——每日最新资讯28at.com

迭代器这一新特性,有认同也有否定。无论怎么说,Go 新的复杂度来了。今天分享他针对 Go 在 rsc 当权后的现状的看法和对迭代器的不满等看法的文章。SZE28资讯网——每日最新资讯28at.com

本文原作者 Aliaksandr Valialkin 是 vm、quicktemplate、fastjson、fasthttp、fastcache、easyproto 等的开发者,也算是较为资深的 Go 工程师了。SZE28资讯网——每日最新资讯28at.com

Go 编程语言因其易用性而广为人知。归功于其精心设计的语法、特性和工具,Go 使得编写任意复杂度的易于阅读和维护的程序成为可能(详见 GitHub)。SZE28资讯网——每日最新资讯28at.com

图片图片SZE28资讯网——每日最新资讯28at.com

然而,一些软件工程师称 Go 为 “无聊”和 “过时”,因为它缺少像:monads、option types、LINQ、borrow checkers、零开销抽象、面向方面编程、继承、函数和运算符重载等特性。SZE28资讯网——每日最新资讯28at.com

虽然这些特性可能简化特定领域的代码编写,但它们除了带来好处之外,还有额外的成本。SZE28资讯网——每日最新资讯28at.com

这些特性通常对脑力锻炼有好处。但在处理生产代码时,我们不需要额外的心理负担,因为我们已经忙于解决业务任务。SZE28资讯网——每日最新资讯28at.com

所有这些特性的主要成本是:SZE28资讯网——每日最新资讯28at.com

  • • 仅仅通过阅读代码,就更难理解发生了什么;
  • • 调试这样的代码变得更加困难,因为需要跳过数十个非平凡的抽象才能到达业务逻辑;
  • • 由于这些特性施加的限制,向这样的代码添加新功能变得更加困难。

这可能会显著减慢甚至停止代码开发的步伐。这就是 Go 最初没有这些特性的主要原因。SZE28资讯网——每日最新资讯28at.com

泛型在 Go1.18 中的引入

不幸的是,这些特性开始出现在最近的 Go 版本中:SZE28资讯网——每日最新资讯28at.com

泛型已经在 Go1.18 中添加。许多软件工程师希望在 Go 中添加泛型,因为他们认为这将显著提高他们在 Go 中的生产力。SZE28资讯网——每日最新资讯28at.com

自 Go1.18 发布以来已经过去了两年,但生产力提高的迹象并不明显。Go 中泛型的总体采用率仍然很低。SZE28资讯网——每日最新资讯28at.com

为什么?因为在大多数实际的 Go 代码中并不需要泛型。SZE28资讯网——每日最新资讯28at.com

另一方面,泛型显著增加了 Go 语言本身的复杂性。例如,尝试理解泛型添加后 Go 类型推断的所有细节。它的复杂性已经非常接近 C++ 类型推断了。SZE28资讯网——每日最新资讯28at.com

Go 泛型还缺少 C++ 模板中存在的基本特性,例如,Go 泛型不支持泛型类型中的泛型方法。它们也不支持模板特化和模板模板参数,以及许多其他特性,这些特性需要充分利用泛型编程。SZE28资讯网——每日最新资讯28at.com

Go 1.23 中的迭代器

Range over functions(范围函数),即迭代器、生成器或协程,将在 Go 1.23 中被添加。让我们更仔细地看看这个 “特性”。SZE28资讯网——每日最新资讯28at.com

如果你不熟悉 Go 中的迭代器,那么请阅读这篇介绍[3]。SZE28资讯网——每日最新资讯28at.com

本质上,这是一种语法糖,允许在具有特殊签名的函数上编写 for ... range 循环。这听起来像是一个很棒的特性,不是吗?让我们尝试弄清楚这个特性解决了什么实际问题。SZE28资讯网——每日最新资讯28at.com

在 Go 中,没有一种标准的方式来遍历一系列值。由于缺乏任何约定,最终出现了各种各样的方法。SZE28资讯网——每日最新资讯28at.com

每种实现都做了在当时的上下文中最有意义的事,但孤立的决策导致了用户的困惑。SZE28资讯网——每日最新资讯28at.com

以下是一些标准库中存在的不同迭代方式:SZE28资讯网——每日最新资讯28at.com

• archive/tar.Reader.NextSZE28资讯网——每日最新资讯28at.com

• bufio.Reader.ReadByteSZE28资讯网——每日最新资讯28at.com

• bufio.Scanner.ScanSZE28资讯网——每日最新资讯28at.com

• container/ring.Ring.DoSZE28资讯网——每日最新资讯28at.com

• database/sql.RowsSZE28资讯网——每日最新资讯28at.com

• expvar.DoSZE28资讯网——每日最新资讯28at.com

• flag.VisitSZE28资讯网——每日最新资讯28at.com

• go/token.FileSet.IterateSZE28资讯网——每日最新资讯28at.com

• path/filepath.WalkSZE28资讯网——每日最新资讯28at.com

• runtime.Frames.NextSZE28资讯网——每日最新资讯28at.com

• sync.Map.RangeSZE28资讯网——每日最新资讯28at.com

这些函数在迭代的细节上几乎没有一致性。即使在签名上达成一致的函数也不总是同意语义。SZE28资讯网——每日最新资讯28at.com

例如,大多数返回 (bool, T) 的迭代函数遵循 Go 的常规,即第一个 bool 表示第二个 T 是否有效。SZE28资讯网——每日最新资讯28at.com

与此相反,runtime.Frames.Next 返回的 bool 表示迭代是否继续。SZE28资讯网——每日最新资讯28at.com

当你想要遍历某些内容时,你首先必须了解你正在调用的特定代码如何处理迭代。这种不一致性阻碍了 Go 的目标,即在大型代码库中轻松移动。SZE28资讯网——每日最新资讯28at.com

人们经常提到所有 Go 代码看起来都差不多是一种优势。但对于具有自定义迭代的代码来说,这根本不是真的。SZE28资讯网——每日最新资讯28at.com

同理,这听起来是合理的 - 在 Go 中有一种统一的方式来遍历各种类型。但是,关于向后兼容性,Go 的主要优势之一呢?SZE28资讯网——每日最新资讯28at.com

所有上述标准库中的现有自定义迭代器将根据 Go 兼容性规则永远保留在标准库中。SZE28资讯网——每日最新资讯28at.com

因此,所有新的 Go 版本将至少提供两种不同的方式,在标准库中遍历各种类型 - 旧的方式和新的方式。SZE28资讯网——每日最新资讯28at.com

这增加了 Go 编程的复杂性,因为:SZE28资讯网——每日最新资讯28at.com

  • • 你需要知道遍历各种类型时的两种方式,而不是单一的方式。
  • • 你需要能够阅读和维护使用旧迭代器的旧代码,以及可能使用旧迭代器或新迭代器,或同时使用两种迭代器类型的新代码。
  • • 当你编写新代码时,需要选择适当的迭代器类型。

Go 1.23 中迭代器的其他问题

直到 Go 1.23,for ... range 循环只能应用于内置类型:int(自 Go1.22 起)、string、slice、map 和 channel。SZE28资讯网——每日最新资讯28at.com

这些循环的语义清晰易懂(channel 的循环语义更复杂,但如果你处理并发编程,那么你应该很容易理解)。SZE28资讯网——每日最新资讯28at.com

for ... range 循环现在可以应用于函数,这引入了一种新形式的迭代器,称为 pull 和 push 函数。SZE28资讯网——每日最新资讯28at.com

这使得不可能理解给定的无辜循环体 for ... range 实际上在背后做什么,因为它隐式地将循环体包装在一个匿名函数中,并将其传递给 push 迭代器函数。此外,它隐式地调用匿名 pull 函数,并将返回的结果传递给循环体。SZE28资讯网——每日最新资讯28at.com

这还隐式地转换了 return、continue、break、goto 和 defer 语句为匿名函数内的非显式语句。SZE28资讯网——每日最新资讯28at.com

以下是一些示例代码,展示了新迭代器的使用和潜在问题:SZE28资讯网——每日最新资讯28at.com

// 旧的显式回调方法tree.walk(func(k, v string) { println(k, v) })// 新的 range over function 方法for k, v := range tree.walk { println(k, v) }

请记住,后面的循环隐式地转换为带有显式回调调用的前一个代码。现在让我们从循环中返回一些东西:SZE28资讯网——每日最新资讯28at.com

for k, v := range tree.walk {  if k == "foo" {    return v  }}

它隐式地转换为类似于以下内容的难以追踪的代码:SZE28资讯网——每日最新资讯28at.com

var vOuter stringneedOuterReturn := falsetree.walk(func(k, v string) bool {  if k == "foo" {    needOuterReturn = true    vOuter = strings.Clone(v) // 这里进行了内存分配和复制    return false  }})if needOuterReturn {  return vOuter}

如果 tree.walk 通过从字节切片的不安全转换将 v 传递给回调,那么 v 的内容可能在下一次循环迭代中改变。因此,隐式生成的防弹代码必须使用 strings.Clone(),这可能导致不必要的内存分配和复制。SZE28资讯网——每日最新资讯28at.com

range over func 特性对函数签名施加了限制。这些限制并不适合所有需要迭代集合项的情况。SZE28资讯网——每日最新资讯28at.com

这迫使软件工程师在 for ... range 循环的丑陋 hack 和编写理想适合给定任务的显式代码之间做出艰难选择。SZE28资讯网——每日最新资讯28at.com

结论

遗憾的是,Go 开始朝着增加复杂性和隐式代码执行的方向发展。也许我们需要停止添加增加 Go 复杂性的特性,而应该专注于 Go 的基本特性 - 简单性、生产力和性能。SZE28资讯网——每日最新资讯28at.com

例如,最近 Rust 开始在性能关键领域取代 Go 的份额。我相信如果 Go 核心团队专注于热点循环的优化,如循环展开和 SIMD 使用,这种趋势可以逆转。SZE28资讯网——每日最新资讯28at.com

这不应该太影响编译和链接速度,因为只有一小部分编译的 Go 代码需要优化。没有必要优化所有变体的愚蠢代码 - 这些代码即使在优化热点循环之后,仍然会很慢。SZE28资讯网——每日最新资讯28at.com

只优化软件工程师有意编写的特定模式就足够了,他们关心他们的代码性能。SZE28资讯网——每日最新资讯28at.com

Go 比 Rust 更容易使用。我们为什么要在性能竞赛中输给 Rust?SZE28资讯网——每日最新资讯28at.com

另一个 Go 可以在不增加语言本身复杂性和使用这些特性的 Go 代码复杂性的情况下获得的有用特性的例子,是类似于这个的小生活质量改进。SZE28资讯网——每日最新资讯28at.com

我是谁?

我是 VictoriaMetrics、quicktemplate、fastjson、fasthttp、fastcache、easyproto 等的开发者。感谢 Go,我一直在尝试遵循 KISS 设计原则。SZE28资讯网——每日最新资讯28at.com

引用链接

[1] Aliaksandr Valialkin: https://github.com/valyalaSZE28资讯网——每日最新资讯28at.com

[2] 文章: https://itnext.io/go-evolves-in-the-wrong-direction-7dfda8a1a620SZE28资讯网——每日最新资讯28at.com

[3] 介绍: https://bitfieldconsulting.com/posts/iteratorsSZE28资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-96764-0.html业内大佬怒喷:Go 正朝着错误的方向发展

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: CSS的媒体查询:响应式布局的利器

下一篇: 聊聊性能指标CPU利用率如何计算的?

标签:
  • 热门焦点
  • 容量越大越不坏?24万块硬盘故障率报告公布 这些产品零故障

    容量越大越不坏?24万块硬盘故障率报告公布 这些产品零故障

    8月5日消息,云存储服务商Backblaze发布了最新的硬盘故障率报告,年故障率有所上升。Backblaze发布的硬盘季度统计数据,其中包括故障率等重要方面。这些结
  • JavaScript 混淆及反混淆代码工具

    JavaScript 混淆及反混淆代码工具

    介绍在我们开始学习反混淆之前,我们首先要了解一下代码混淆。如果不了解代码是如何混淆的,我们可能无法成功对代码进行反混淆,尤其是使用自定义混淆器对其进行混淆时。什么是混
  • 摸鱼心法第一章——和配置文件说拜拜

    摸鱼心法第一章——和配置文件说拜拜

    为了能摸鱼我们团队做了容器化,但是带来的问题是服务配置文件很麻烦,然后大家在群里进行了“亲切友好”的沟通图片图片图片图片对比就对比,简单对比下独立配置中心和k8s作为配
  • SpringBoot中使用Cache提升接口性能详解

    SpringBoot中使用Cache提升接口性能详解

    环境:springboot2.3.12.RELEASE + JSR107 + Ehcache + JPASpring 框架从 3.1 开始,对 Spring 应用程序提供了透明式添加缓存的支持。和事务支持一样,抽象缓存允许一致地使用各
  • 一年经验在二线城市面试后端的经验分享

    一年经验在二线城市面试后端的经验分享

    忠告这篇文章只适合2年内工作经验、甚至没有工作经验的朋友阅读。如果你是2年以上工作经验,请果断划走,对你没啥帮助~主人公这篇文章内容来自 「升职加薪」星球星友 的投稿,坐
  • 只需五步,使用start.spring.io快速入门Spring编程

    只需五步,使用start.spring.io快速入门Spring编程

    步骤1打开https://start.spring.io/,按照屏幕截图中的内容创建项目,添加 Spring Web 依赖项,并单击“生成”按钮下载 .zip 文件,为下一步做准备。请在进入步骤2之前进行解压。图
  • 梁柱接棒两年,腾讯音乐闯出新路子

    梁柱接棒两年,腾讯音乐闯出新路子

    文丨田静 出品丨牛刀财经(niudaocaijing)7月5日,企鹅FM发布官方公告称由于业务调整,将于9月6日正式停止运营,这意味着腾讯音乐长音频业务走向消亡。腾讯在长音频领域还在摸索。为
  • AI芯片初创公司Tenstorrent获三星和现代1亿美元投资

    AI芯片初创公司Tenstorrent获三星和现代1亿美元投资

    Tenstorrent是一家由芯片行业资深人士Jim Keller领导的加拿大初创公司,专注于开发人工智能芯片,该公司周三表示,已经从现代汽车集团和三星投资基金等
  • DRAM存储器10月价格下跌,NAND闪存本月价格与上月持平

    DRAM存储器10月价格下跌,NAND闪存本月价格与上月持平

    10月30日,据韩国媒体消息,自今年年初以来一直在上涨的 DRAM 存储器的交易价格仅在本月就下跌了近 10%,此次是全年首次降价,而NAND 闪存本月价格与上月持平。市
Top