<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Tony Bai &#187; 语法</title>
	<atom:link href="http://tonybai.com/tag/%e8%af%ad%e6%b3%95/feed/" rel="self" type="application/rss+xml" />
	<link>https://tonybai.com</link>
	<description>一个程序员的心路历程</description>
	<lastBuildDate>Mon, 08 Jun 2026 23:32:23 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>Go 作为第一门编程语言：天才之选还是糟糕开端？</title>
		<link>https://tonybai.com/2025/10/11/go-is-a-good-first-programming-language/</link>
		<comments>https://tonybai.com/2025/10/11/go-is-a-good-first-programming-language/#comments</comments>
		<pubDate>Sat, 11 Oct 2025 00:14:22 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[C]]></category>
		<category><![CDATA[Channel]]></category>
		<category><![CDATA[GC]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[goroutine]]></category>
		<category><![CDATA[iferrnil]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[malloc/free]]></category>
		<category><![CDATA[Pointer]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[reference]]></category>
		<category><![CDATA[TypeScript]]></category>
		<category><![CDATA[专业开发流程]]></category>
		<category><![CDATA[两大阵营]]></category>
		<category><![CDATA[中间地带]]></category>
		<category><![CDATA[交互式体验]]></category>
		<category><![CDATA[伪代码]]></category>
		<category><![CDATA[健壮性]]></category>
		<category><![CDATA[兴趣培养]]></category>
		<category><![CDATA[内存]]></category>
		<category><![CDATA[内存管理]]></category>
		<category><![CDATA[初学者]]></category>
		<category><![CDATA[垃圾回收]]></category>
		<category><![CDATA[堆]]></category>
		<category><![CDATA[天才之选]]></category>
		<category><![CDATA[失败]]></category>
		<category><![CDATA[契约]]></category>
		<category><![CDATA[安全的指针哲学]]></category>
		<category><![CDATA[安全网]]></category>
		<category><![CDATA[实战派]]></category>
		<category><![CDATA[工程思想]]></category>
		<category><![CDATA[并发]]></category>
		<category><![CDATA[底层]]></category>
		<category><![CDATA[引用vs值]]></category>
		<category><![CDATA[快速入门]]></category>
		<category><![CDATA[快速反馈]]></category>
		<category><![CDATA[性能]]></category>
		<category><![CDATA[抽象]]></category>
		<category><![CDATA[指针]]></category>
		<category><![CDATA[指针算术]]></category>
		<category><![CDATA[捷径]]></category>
		<category><![CDATA[摩擦力]]></category>
		<category><![CDATA[数据结构]]></category>
		<category><![CDATA[显式错误处理]]></category>
		<category><![CDATA[构建时]]></category>
		<category><![CDATA[栈]]></category>
		<category><![CDATA[核心分歧]]></category>
		<category><![CDATA[现代计算机科学]]></category>
		<category><![CDATA[甜蜜点]]></category>
		<category><![CDATA[算法]]></category>
		<category><![CDATA[糟糕开端]]></category>
		<category><![CDATA[纪律训练]]></category>
		<category><![CDATA[经验丰富的开发者]]></category>
		<category><![CDATA[编程教育]]></category>
		<category><![CDATA[编程语言]]></category>
		<category><![CDATA[编译周期]]></category>
		<category><![CDATA[解释型语言]]></category>
		<category><![CDATA[计算机科学基础教育]]></category>
		<category><![CDATA[计算机科学家]]></category>
		<category><![CDATA[认知门槛]]></category>
		<category><![CDATA[语法]]></category>
		<category><![CDATA[软件工程师]]></category>
		<category><![CDATA[运行时]]></category>
		<category><![CDATA[静态类型]]></category>
		<category><![CDATA[项目结构]]></category>
		<category><![CDATA[黄金标准]]></category>
		<category><![CDATA[黑盒]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=5240</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2025/10/11/go-is-a-good-first-programming-language 大家好，我是Tony Bai。 近日，在 r/golang 社区，一个初学者的真诚提问，再次点燃了一场关于 Go 是否适合作为入门语言的激烈辩论。他很困惑：“为什么很多经验丰富的开发者说 Go 不适合作为第一门编程语言，而很多大学却用与之相似的 C 语言作为第一门编程语言呢？” 这个问题，如同一块探针，深入到了编程教育的核心分歧之中，并迅速将社区观点分裂为两大阵营。一方认为，Go 能从第一天起就培养严谨的工程思维，堪称“天才之选”。另一方则认为，它的定位不上不下，对初学者而言是一个“糟糕的开端”。 那么，真相究竟为何？为了厘清思路，让我们深入这场辩论，分别听取两大阵营的观点，并审视其背后的根本分歧：我们学习编程，到底是为了什么？ 观点一：Go 是一个“糟糕的开端” 这一方的核心论点是：Go 语言陷入了一个尴尬的“中间地带”，对于编程教育的两个主要目标，它都未能完美胜任。 论据一：Go 不够底层，无法胜任“计算机科学基础教育” 这一方的支持者指出，大学 CS 教育的首要目标，是培养学生对计算机工作原理的深刻理解。在这个目标下，C 语言之所以是“黄金标准”，恰恰在于它的“不友好”： 直面内存：手动 malloc/free 和危险的指针算术，迫使学生直面内存布局、栈与堆等核心概念。 最小化抽象：学生必须从零开始构建数据结构，这个过程能让他们对算法的理解建立在物理实现之上。 而Go 的垃圾回收 (GC) 机制，虽然是工程上的巨大进步，但在教育上却成了一个“黑盒”，完全隐藏了内存管理的复杂性。它让学生“知其然”，却无法“知其所以然”，因此无法胜任传授底层原理的重任。 论据二：Go 不够“温柔”，无法胜任“快速入门与兴趣培养” 接着，这一方展示了另一个极端——以 Python 为代表的“实战派”入门语言。这类语言的目标是让初学者尽快体验到编程的乐趣和效用。 语法“温柔”：Python 的语法接近伪代码，极大地降低了入门的认知门槛。 快速反馈：作为解释型语言，其“编写即运行”的交互式体验，对维持初学者的学习热情至关重要。 尽管 Go 也以简单著称，但其静态类型、编译周期、以及对项目结构的规范要求，都为纯粹的初学者制造了不必要的“摩擦力”。与 Python 相比，它不够“温柔”，可能会在入门阶段就劝退一部分学习者。 由此来看，Go 既不像 C 那样能让你深入底层，又不像 Python 那样能让你轻松起步。它是一个尴尬的“中间派”，对于任何一个明确的教学目标来说，都有比它更好的选择。因此，它是一个“糟糕的开端”。 观点二：Go [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/2025/go-is-a-good-first-programming-language-1.png" alt="" /></p>
<p><a href="https://tonybai.com/2025/10/11/go-is-a-good-first-programming-language">本文永久链接</a> &#8211; https://tonybai.com/2025/10/11/go-is-a-good-first-programming-language</p>
<p>大家好，我是Tony Bai。</p>
<p>近日，在 r/golang 社区，<a href="https://www.reddit.com/r/golang/comments/1nvbrv8/im_confused_as_to_why_experienced_devs_say_go_is/">一个初学者的真诚提问</a>，再次点燃了一场关于 Go 是否适合作为入门语言的激烈辩论。他很困惑：“为什么很多经验丰富的开发者说 Go 不适合作为第一门编程语言，而很多大学却用与之相似的 C 语言作为第一门编程语言呢？”</p>
<p>这个问题，如同一块探针，深入到了编程教育的核心分歧之中，并迅速将社区观点分裂为两大阵营。一方认为，Go 能从第一天起就培养严谨的工程思维，堪称<strong>“天才之选”</strong>。另一方则认为，它的定位不上不下，对初学者而言是一个<strong>“糟糕的开端”</strong>。</p>
<p>那么，真相究竟为何？为了厘清思路，让我们深入这场辩论，分别听取两大阵营的观点，并审视其背后的根本分歧：<strong>我们学习编程，到底是为了什么？</strong></p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/paid/go-network-programming-complete-guide-pr.png" alt="" /></p>
<h2>观点一：Go 是一个“糟糕的开端”</h2>
<p>这一方的核心论点是：Go 语言陷入了一个尴尬的“中间地带”，对于编程教育的两个主要目标，它都未能完美胜任。</p>
<h3>论据一：Go 不够底层，无法胜任“计算机科学基础教育”</h3>
<p>这一方的支持者指出，大学 CS 教育的首要目标，是培养学生对计算机工作原理的深刻理解。在这个目标下，C 语言之所以是“黄金标准”，恰恰在于它的“不友好”：</p>
<ul>
<li><strong>直面内存</strong>：手动 malloc/free 和危险的指针算术，迫使学生直面内存布局、栈与堆等核心概念。</li>
<li><strong>最小化抽象</strong>：学生必须从零开始构建数据结构，这个过程能让他们对算法的理解建立在物理实现之上。</li>
</ul>
<p>而Go 的<strong>垃圾回收 (GC)</strong> 机制，虽然是工程上的巨大进步，但在教育上却成了一个“黑盒”，<strong>完全隐藏了内存管理的复杂性</strong>。它让学生“知其然”，却无法“知其所以然”，因此无法胜任传授底层原理的重任。</p>
<h3>论据二：Go 不够“温柔”，无法胜任“快速入门与兴趣培养”</h3>
<p>接着，这一方展示了另一个极端——以 Python 为代表的“实战派”入门语言。这类语言的目标是让初学者尽快体验到编程的乐趣和效用。</p>
<ul>
<li><strong>语法“温柔”</strong>：Python 的语法接近伪代码，极大地降低了入门的认知门槛。</li>
<li><strong>快速反馈</strong>：作为解释型语言，其“编写即运行”的交互式体验，对维持初学者的学习热情至关重要。</li>
</ul>
<p>尽管 Go 也以简单著称，但其<strong>静态类型、编译周期、以及对项目结构的规范要求</strong>，都为纯粹的初学者制造了不必要的“摩擦力”。与 Python 相比，它不够“温柔”，可能会在入门阶段就劝退一部分学习者。</p>
<p>由此来看，Go 既不像 C 那样能让你深入底层，又不像 Python 那样能让你轻松起步。它是一个尴尬的“中间派”，对于任何一个明确的教学目标来说，都有比它更好的选择。因此，它是一个“糟糕的开端”。</p>
<h2>观点二：Go 是一个“天才之选”</h2>
<p>另一方的核心论点是：观点一中所说的“中间地带”并非尴尬，而是一个<strong>经过深思熟虑、精心设计的“甜蜜点” (sweet spot)</strong>。Go 的目标，不是培养纯粹的理论家或业余爱好者，而是从第一天起，就<strong>为培养专业的“软件工程师”奠定基础</strong>。</p>
<h3>论据一：Go 教授的是“更重要”的底层原理</h3>
<p>观点二的支持者承认 Go 隐藏了手动内存管理的细节，但他们认为，在 2025 年的今天，这部分细节的教学价值正在下降。相反，Go 教授了更现代、更重要的底层概念：</p>
<ul>
<li><strong>安全的指针哲学</strong>：Go 保留了指针，让学生能够深刻理解<strong>“引用 vs. 值”</strong>这一核心概念，这是理解程序性能和行为的关键。同时，它通过移除指针算术，杜绝了 C 语言中最常见的一类安全漏洞。</li>
<li><strong>并发是第一性原理</strong>：他们强调，现代计算的核心是并发。Go 将 goroutine 和 channel 作为内建特性，让学生能够以一种前所未有的简洁方式，去接触和理解并发这一现代计算机科学的基石。</li>
</ul>
<p>Go 并非不教底层，而是有选择地教授那些<strong>在现代软件工程中依然至关重要的底层概念</strong>，同时将那些日益自动化、易出错的细节（如手动内存管理）抽象掉。</p>
<h3>论据二：Go 的“摩擦力”恰恰是良好工程习惯的开端</h3>
<p>观点二的支持者认为，观点一所说的“摩擦力”，实际上是宝贵的“纪律训练”：</p>
<ul>
<li><strong>静态类型</strong>：不是负担，而是一张安全网，它教会学生思考数据的结构和契约。TypeScript逐步超越JavaScript就是一个静态类型取得胜利的明证。</li>
<li><strong>显式错误处理</strong>：if err != nil 不是样板代码，而是对健壮性最深刻的、日复一日的训练。它让学生明白，<strong>失败是程序中正常的一部分，必须被认真对待</strong>。</li>
<li><strong>编译周期</strong>：不是障碍，而是专业开发流程的预演，教会学生区分构建时和运行时。</li>
</ul>
<p>Go 的设计，完美地平衡了抽象与细节。它既能让学生快速构建出实际的应用（比如一个简单的 Web 服务器），又在整个过程中不断地、潜移默化地向他们灌输专业的工程思想。它不是在教“编程”，而是在教“软件工程”。因此，对于立志成为专业工程师的学习者来说，它是一个<strong>“天才之选”</strong>。</p>
<h2>小结：目标决定了最佳路径</h2>
<p>至此，辩论的脉络已经清晰。这场争论没有绝对的赢家，因为双方的论点都建立在各自合理的目标之上。</p>
<p><strong>最终的结论是：这取决于你的目标。</strong></p>
<ul>
<li>如果你的目标是<strong>成为一名计算机科学家</strong>，深入理解机器的每一个齿轮如何运转，那么从 C 开始的“苦修”或许无法绕开。</li>
<li>如果你的目标是<strong>快速体验编程的乐趣、尽快构建应用</strong>，那么 Python 或 JavaScript 可能会为你提供一条更平坦、更愉悦的道路。</li>
<li>而 Go，则为那些从一开始就立志于<strong>成为一名专业、高效、能构建并发系统的现代软件工程师</strong>的学习者，提供了一条无与伦比的捷径。</li>
</ul>
<p>它或许不是最完美的“第一站”，但对于目标明确的人来说，它是一个能让你赢在起跑线上的<strong>“天才之选”</strong>。它将“学习编程”与“成为一名软件工程师”这两个阶段，以前所未有的方式紧密地结合在了一起。</p>
<p>资料链接：https://www.reddit.com/r/golang/comments/1nvbrv8/im_confused_as_to_why_experienced_devs_say_go_is/</p>
<hr />
<p><strong>想系统学习Go，构建扎实的知识体系？</strong></p>
<p>我的新书《<a href="https://book.douban.com/subject/37499496/">Go语言第一课</a>》是你的首选。源自2.4万人好评的极客时间专栏，内容全面升级，同步至Go 1.24。首发期有专属五折优惠，不到40元即可入手，扫码即可拥有这本300页的Go语言入门宝典，即刻开启你的Go语言高效学习之旅！</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/go-primer-published-4.png" alt="" /></p>
<hr />
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求，请扫描下方公众号二维码，与我私信联系。</p>
<p><img src="http://image.tonybai.com/img/tonybai/iamtonybai-wechat-qr.png" alt="" /></p>
<p style='text-align:left'>&copy; 2025, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2025/10/11/go-is-a-good-first-programming-language/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Go 1.25中值得关注的几个变化</title>
		<link>https://tonybai.com/2025/08/15/some-changes-in-go-1-25/</link>
		<comments>https://tonybai.com/2025/08/15/some-changes-in-go-1-25/#comments</comments>
		<pubDate>Fri, 15 Aug 2025 00:21:19 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[Cgroup]]></category>
		<category><![CDATA[container]]></category>
		<category><![CDATA[CoreType]]></category>
		<category><![CDATA[CPU]]></category>
		<category><![CDATA[DWARF5]]></category>
		<category><![CDATA[encoding]]></category>
		<category><![CDATA[GC]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[go-import]]></category>
		<category><![CDATA[go.mod]]></category>
		<category><![CDATA[Go1]]></category>
		<category><![CDATA[go1.24]]></category>
		<category><![CDATA[godoc]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[GOMAXPROCS]]></category>
		<category><![CDATA[goroutine]]></category>
		<category><![CDATA[govet]]></category>
		<category><![CDATA[GPU]]></category>
		<category><![CDATA[ignore]]></category>
		<category><![CDATA[json]]></category>
		<category><![CDATA[jsonv2]]></category>
		<category><![CDATA[k8s]]></category>
		<category><![CDATA[limit]]></category>
		<category><![CDATA[map]]></category>
		<category><![CDATA[marshal]]></category>
		<category><![CDATA[monorepo]]></category>
		<category><![CDATA[nil]]></category>
		<category><![CDATA[pprof]]></category>
		<category><![CDATA[runtime]]></category>
		<category><![CDATA[spec]]></category>
		<category><![CDATA[subdir]]></category>
		<category><![CDATA[swisstable]]></category>
		<category><![CDATA[sync]]></category>
		<category><![CDATA[synctest]]></category>
		<category><![CDATA[Testing]]></category>
		<category><![CDATA[toolchain]]></category>
		<category><![CDATA[unmarshal]]></category>
		<category><![CDATA[vanity-import]]></category>
		<category><![CDATA[waitgroup]]></category>
		<category><![CDATA[兼容性]]></category>
		<category><![CDATA[单一仓库]]></category>
		<category><![CDATA[垃圾回收]]></category>
		<category><![CDATA[容器]]></category>
		<category><![CDATA[工具链]]></category>
		<category><![CDATA[指针]]></category>
		<category><![CDATA[标准库]]></category>
		<category><![CDATA[泛型]]></category>
		<category><![CDATA[流式]]></category>
		<category><![CDATA[特性]]></category>
		<category><![CDATA[空指针]]></category>
		<category><![CDATA[编解码]]></category>
		<category><![CDATA[规范]]></category>
		<category><![CDATA[语法]]></category>
		<category><![CDATA[运行时]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=5037</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2025/08/15/some-changes-in-go-1-25 大家好，我是Tony Bai。 北京时间2025年8月13日，Go 团队如期发布了 Go 语言的最新大版本——Go 1.25。按照惯例，每次 Go 大版本发布时，我都会撰写一篇“Go 1.x 中值得关注的几个变化”的文章。自 2014 年的 Go 1.4 版本起，这一系列文章已经伴随大家走过了十一个年头。 不过，随着我在版本冻结前推出的“Go 1.x 新特性前瞻”系列，以及对该大版本可能加入特性的一些独立的解读文章，本系列文章的形式也在不断演变。本文将不再对每个特性进行细致入微的分析，因为这些深度内容大多已在之前的《Go 1.25 新特性前瞻》一文中详细讨论过。本文将更聚焦于提炼核心亮点，并分享一些我的思考。 好了，言归正传，我们来看看Go 1.25带来了哪些惊喜！ 语言变化：兼容性基石上的精雕细琢 正如 Go 一贯所做的，新版 Go 1.25 继续遵循 Go1 的兼容性规范。最令 Gopher 们安心的一点是：Go 1.25 没有引入任何影响现有 Go 程序的语言级变更。 There are no languages changes that affect Go programs in Go 1.25. 这种对稳定性的极致追求，是 Go [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/2025/some-changes-in-go-1-25-1.png" alt="" /></p>
<p><a href="https://tonybai.com/2025/08/15/some-changes-in-go-1-25">本文永久链接</a> &#8211; https://tonybai.com/2025/08/15/some-changes-in-go-1-25</p>
<p>大家好，我是Tony Bai。</p>
<p>北京时间2025年8月13日，Go 团队如期发布了 Go 语言的最新大版本——<a href="https://go.dev/blog/go1.25">Go 1.25</a>。按照惯例，每次 Go 大版本发布时，我都会撰写一篇“Go 1.x 中值得关注的几个变化”的文章。自 2014 年的 <a href="https://tonybai.com/2014/11/04/some-changes-in-go-1-4">Go 1.4 版本</a>起，这一系列文章已经伴随大家走过了十一个年头。</p>
<p>不过，随着我在版本冻结前推出的“Go 1.x 新特性前瞻”系列，以及对该大版本可能加入特性的一些独立的解读文章，本系列文章的形式也在不断演变。本文将不再对每个特性进行细致入微的分析，因为这些深度内容大多已在之前的<a href="https://tonybai.com/2025/06/14/go-1-25-foresight">《Go 1.25 新特性前瞻》</a>一文中详细讨论过。本文将更聚焦于提炼核心亮点，并分享一些我的思考。</p>
<p>好了，言归正传，我们来看看Go 1.25带来了哪些惊喜！</p>
<h2>语言变化：兼容性基石上的精雕细琢</h2>
<p>正如 Go 一贯所做的，新版 Go 1.25 继续遵循 <a href="https://go.dev/doc/go1compat">Go1 的兼容性规范</a>。最令 Gopher 们安心的一点是：<strong>Go 1.25 没有引入任何影响现有 Go 程序的语言级变更</strong>。</p>
<blockquote>
<p>There are no languages changes that affect Go programs in Go 1.25.</p>
</blockquote>
<p>这种对稳定性的极致追求，是 Go 成为生产环境首选语言之一的重要原因。</p>
<p>尽管语法层面波澜不惊，但语言规范内部却进行了一次“大扫除”——<strong>移除了“core types”的概念</strong>。这一变化虽然对日常编码无直接影响，但它简化了语言规范，为未来泛型可能的演进铺平了道路，体现了 Go 团队在设计层面的严谨与远见。关于此变化的深度解读，可以回顾我之前的文章《<a href="https://tonybai.com/2025/03/27/remove-coretypes-from-go-spec/">Go 1.25 规范大扫除：移除“Core Types”，为更灵活的泛型铺路</a>》。</p>
<h2>编译器与运行时：看不见的性能飞跃</h2>
<p>如果说 <a href="https://tonybai.com/2025/02/16/some-changes-in-go-1-24">Go 1.24</a> 的运行时核心是<a href="https://tonybai.com/2024/11/14/go-map-use-swiss-table">优化 map</a>，那么 Go 1.25 的灵魂则在于让 Go 程序更“懂”其运行环境，并对 GC 进行了大刀阔斧的革新。</p>
<h3>容器感知型 GOMAXPROCS</h3>
<p>这无疑是 Go 1.25 最具影响力的变化之一。在容器化部署已成事实标准的今天，Go 1.25 的运行时终于具备了 <strong>cgroup 感知能力</strong>。在 Linux 系统上，它会默认根据容器的 CPU limit 来设置 GOMAXPROCS，并能动态适应 limit 的变化。</p>
<p>这意味着，只需升级到 Go 1.25，你的 Go 应用在 K8s 等环境中的 CPU 资源使用将变得更加智能和高效，告别了过去因 GOMAXPROCS 默认值不当而导致的资源浪费或性能瓶颈。更多细节，请参阅我的文章《<a href="https://tonybai.com/2025/04/09/gomaxprocs-defaults-add-cgroup-aware/">Go 1.25 新提案：GOMAXPROCS 默认值将迎 Cgroup 感知能力，终结容器性能噩梦？</a>》。</p>
<h3>实验性的 Green Tea GC</h3>
<p>Go 1.25 迈出了 GC 优化的重要一步，引入了一个新的实验性垃圾收集器。通过设置 GOEXPERIMENT=greenteagc 即可在构建时启用。</p>
<blockquote>
<p>A new garbage collector is now available as an experiment. This garbage collector’s design improves the performance of marking and scanning small objects through better locality and CPU scalability.</p>
</blockquote>
<p>据官方透露，这个新 GC 有望为真实世界的程序带来 <strong>10%—40% 的 GC 开销降低</strong>。知名go开发者Josh Baker(@tidwall)在Go 1.25发布正式版后，在X上分享了自己使用go 1.25新gc（绿茶）后的结果，他开源的实时地理空间和地理围栏项目tile38的GC开销下降35%：</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/some-changes-in-go-1-25-2.png" alt="" /></p>
<p>这是一个巨大的性能红利，尤其对于重度依赖GC的内存密集型应用。虽然它仍在实验阶段，但其展现的潜力已足够令人兴奋。对 Green Tea GC 设计原理感兴趣的朋友，可以阅读我的文章《<a href="https://tonybai.com/2025/05/03/go-green-tea-garbage-collector/">Go 新垃圾回收器登场：Green Tea GC 如何通过内存感知显著降低 CPU 开销？</a>》。</p>
<p>此外，Go 1.25 还修复了一个存在于 Go 1.21 至 1.24 版本中可能导致 <strong>nil pointer 检查被错误延迟的编译器 bug</strong>，并默认启用了 <strong>DWARFv5 调试信息</strong>，进一步缩小了二进制文件体积并加快了链接速度，对DWARFv5感兴趣的小伙伴儿可以重温一下我之前的《<a href="https://tonybai.com/2025/05/08/go-dwarf5/">Go 1.25链接器提速、执行文件瘦身：DWARF 5调试信息格式升级终落地</a>》一文，了解详情。</p>
<h2>工具链：效率与可靠性的双重提升</h2>
<p>强大的工具链是 Go 生产力的核心保障。Go 1.25 在此基础上继续添砖加瓦。</p>
<h3>go.mod 新增 ignore 指令</h3>
<p>对于大型 Monorepo 项目，go.mod 新增的 ignore 指令是一个福音。它允许你指定 Go 命令在匹配包模式时应忽略的目录，从而在不影响模块依赖的前提下，有效提升大型、混合语言仓库中的构建与扫描效率。关于此特性的详细用法，请见《<a href="https://tonybai.com/2025/05/22/go-mod-ignore-directive/">Go 工具链进化：go.mod 新增 ignore 指令，破解混合项目构建难题</a>》。</p>
<h3>支持仓库子目录作为模块根路径</h3>
<p>一个长期困扰 Monorepo 管理者和自定义 vanity import 用户的难题在 Go 1.25 中也得到了解决。Go 命令现在支持在解析 go-import meta 标签时，通过新增的 subdir 字段，将 Git 仓库中的子目录指定为模块的根。</p>
<p>这意味着，你可以轻松地将 github.com/my-org/my-repo/foo/bar 目录映射为模块路径 my.domain/bar，而无需复杂的代理或目录结构调整。这个看似微小但备受期待的改进，极大地提升了 Go 模块在复杂项目结构中的灵活性。想了解其来龙去脉和具体配置方法，可以参考我的文章《<a href="https://tonybai.com/2025/06/07/allow-serving-module-under-subdir">千呼万唤始出来？Go 1.25解决Git仓库子目录作为模块根路径难题</a>》。</p>
<h3>go doc -http：即开即用的本地文档</h3>
<p>这是一个虽小但美的改进。新的 go doc -http 选项可以快速启动一个本地文档服务器，并在浏览器中直接打开指定对象的文档。对于习惯于离线工作的开发者来说，这极大地提升了查阅文档的便捷性。详细介绍见《<a href="https://tonybai.com/2024/09/06/go-doc-add-http-support/">重拾精髓：go doc -http 让离线包文档浏览更便捷</a>》。</p>
<h3>go vet 新增分析器</h3>
<p>go vet 变得更加智能，新增了两个实用的分析器：</p>
<ul>
<li><strong>waitgroup</strong>：检查 sync.WaitGroup.Add 的调用位置是否错误（例如在 goroutine 内部调用）。</li>
<li><strong>hostport</strong>：诊断不兼容 IPv6 的地址拼接方式 fmt.Sprintf(“%s:%d”, host, port)，并建议使用 net.JoinHostPort。</li>
</ul>
<p>这些静态检查能帮助我们在编码阶段就扼杀掉一批常见的并发和网络编程错误。</p>
<h2>标准库：功能毕业与实验探索</h2>
<p>标准库的演进是每个 Go 版本的重要看点。</p>
<h3>testing/synctest 正式毕业</h3>
<p>在 Go 1.24 中以实验特性登场的 testing/synctest 包，在 Go 1.25 中正式毕业，成为标准库的一员。它为并发代码测试提供了前所未有的利器，通过虚拟化时间和调度，让编写可靠、无 flakiness 的并发测试成为可能。我曾撰写过一个<strong>“<a href="https://mp.weixin.qq.com/mp/appmsgalbum?__biz=MzIyNzM0MDk0Mg==&amp;action=getalbum&amp;album_id=4017357519222882315#wechat_redirect">征服 Go 并发测试</a>”</strong>的微专栏，系统地介绍了该包的设计与实践，欢迎大家订阅学习。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/paid/go-concurrent-test-qr.png" alt="" /></p>
<h3>encoding/json/v2 开启实验</h3>
<p>这是 Go 1.25 最受关注的实验性特性之一！通过 GOEXPERIMENT=jsonv2 环境变量，我们可以启用一个全新的、高性能的 JSON 实现。</p>
<blockquote>
<p>Go 1.25 includes a new, experimental JSON implementation&#8230; The new implementation performs substantially better than the existing one under many scenarios.</p>
</blockquote>
<p>根据官方说明，json/v2 在解码性能上相较于 v1 有了“巨大”的提升。这是 Go 社区多年来对 encoding/json 包性能诟病的一次正面回应。虽然其 API 仍在演进中，但它预示着 Go 的 JSON 处理能力未来将达到新的高度。对 v2 的初探，可以参考我的文章《<a href="https://tonybai.com/2025/05/15/go-json-v2/">手把手带你玩转 GOEXPERIMENT=jsonv2：Go 下一代 JSON 库初探</a>》。jsonv2支持真流式编解码的方法，也可以参考《<a href="https://tonybai.com/2025/08/09/true-streaming-support-in-jsonv2/">Go json/v2实战：告别内存爆炸，掌握真流式Marshal和Unmarshal</a>》这篇文章。</p>
<h3>sync.WaitGroup.Go：并发模式更便捷</h3>
<p>Go 语言的并发编程哲学之一就是让事情保持简单。Go 1.25 在 sync.WaitGroup 上新增的 Go 方法，正是这一哲学的体现。</p>
<p>这个新方法旨在消除 wg.Add(1) 和 defer wg.Done() 这一对经典的样板代码。现在，你可以直接调用 wg.Go(func() { &#8230; }) 来启动一个被 WaitGroup 追踪的 goroutine，Add 和 Done 的调用由 Go 方法在内部自动处理。这不仅让代码更简洁，也从根本上避免了因忘记调用 Add 或 Done 而导致的常见并发错误。</p>
<p>关于这个便捷方法的来龙去脉和设计思考，可以回顾我之前的文章《<a href="https://tonybai.com/2025/04/03/waitgroup-go-proposal/">WaitGroup.Go 要来了？Go 官方提案或让你告别 Add 和 Done 样板代码</a>》。</p>
<h2>其他：Trace Flight Recorder</h2>
<p>最后，我想特别提一下 runtime/trace 包新增的 <strong>Flight Recorder</strong> API。传统的运行时 trace 功能强大但开销巨大，不适合在生产环境中持续开启。</p>
<p>trace.FlightRecorder 提供了一种轻量级的解决方案：它将 trace 数据持续记录到一个内存中的环形缓冲区。当程序中发生某个重要事件（如一次罕见的错误）时，我们可以调用 FlightRecorder.WriteTo 将最近一段时间的 trace 数据快照保存到文件。这种“事后捕获”的模式，使得在生产环境中调试偶发、疑难的性能或调度问题成为可能，是 Go 诊断能力的一次重大升级。更多详情可以参阅《<a href="https://tonybai.com/2025/07/11/net-http-pprof-v2/">Go pprof 迎来重大革新：v2 提案详解，告别默认注册，拥抱飞行记录器</a>》。</p>
<h2>小结</h2>
<p>Go 1.25 的发布，再次彰显了 Go 语言务实求进的核心哲学。它没有追求华而不实的语法糖，而是将精力聚焦于那些能为广大开发者带来“无形收益”的领域：<strong>更智能的运行时、更快的 GC、更可靠的编译器、更高效的工具链</strong>。</p>
<p>这些看似底层的改进，正是 Go 作为一门“生产力语言”的价值所在。它让开发者可以专注于业务逻辑，而将复杂的系统优化和环境适配，放心地交给 Go 语言自身。</p>
<p>我鼓励大家尽快将 Go 1.25 应用到自己的项目中，亲自感受这些变化带来的提升。Go 的旅程，仍在继续，让我们共同期待它在未来创造更多的可能。</p>
<p><strong>感谢阅读！</strong></p>
<p>如果这篇文章让你对 Go 1.25 新特性有了新的认识，请帮忙 <strong>点赞</strong>和<strong>分享</strong>，让更多朋友一起学习和进步！</p>
<hr />
<p>你的Go技能，是否也卡在了“熟练”到“精通”的瓶颈期？</p>
<ul>
<li>想写出更地道、更健壮的Go代码，却总在细节上踩坑？</li>
<li>渴望提升软件设计能力，驾驭复杂Go项目却缺乏章法？</li>
<li>想打造生产级的Go服务，却在工程化实践中屡屡受挫？</li>
</ul>
<p>继《<a href="http://gk.link/a/10AVZ">Go语言第一课</a>》后，我的《<a href="http://gk.link/a/12yGY">Go语言进阶课</a>》终于在极客时间与大家见面了！</p>
<p>我的全新极客时间专栏 《<a href="http://gk.link/a/12yGY">Tony Bai·Go语言进阶课</a>》就是为这样的你量身打造！30+讲硬核内容，带你夯实语法认知，提升设计思维，锻造工程实践能力，更有实战项目串讲。</p>
<p>目标只有一个：助你完成从“Go熟练工”到“Go专家”的蜕变！ 现在就加入，让你的Go技能再上一个新台阶！</p>
<p><img src="https://tonybai.com/wp-content/uploads/course-card/iamtonybai-banner-2.gif" alt="" /></p>
<hr />
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求，请扫描下方公众号二维码，与我私信联系。</p>
<p><img src="http://image.tonybai.com/img/tonybai/iamtonybai-wechat-qr.png" alt="" /></p>
<p style='text-align:left'>&copy; 2025, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2025/08/15/some-changes-in-go-1-25/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>写Go就像喝白开水</title>
		<link>https://tonybai.com/2024/10/29/go-coding-is-like-drinking-boiled-water/</link>
		<comments>https://tonybai.com/2024/10/29/go-coding-is-like-drinking-boiled-water/#comments</comments>
		<pubDate>Tue, 29 Oct 2024 15:22:16 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[思考控]]></category>
		<category><![CDATA[技术志]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[goroutine]]></category>
		<category><![CDATA[Go社区]]></category>
		<category><![CDATA[Go语言第一课]]></category>
		<category><![CDATA[reddit]]></category>
		<category><![CDATA[RussCox]]></category>
		<category><![CDATA[双十一]]></category>
		<category><![CDATA[并发]]></category>
		<category><![CDATA[极客时间]]></category>
		<category><![CDATA[程序员]]></category>
		<category><![CDATA[简单]]></category>
		<category><![CDATA[编程语言]]></category>
		<category><![CDATA[设计哲学]]></category>
		<category><![CDATA[语法]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=4361</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2024/10/29/go-coding-is-like-drinking-boiled-water 在编程语言的世界里，Go语言简单而直接，它没有复杂的语法和华丽的特性，给人一种纯粹的感觉，让我们在编写代码时感受到了一种清晰和高效。 正如Russ Cox所言，Go的“无聊”恰恰是它的优势。抛开冗余装饰，Go专注于可靠、实用的功能。在这个快节奏的时代，它让我们免受复杂性的困扰，帮助我们快速解决实际问题。 写Go就像喝一杯无味的白开水，虽寡淡却能即刻解渴，满足需求，并少有后顾之忧。平淡中透着从容，是我们日常开发中的可靠之选。 早上打开极客时间的首页，发现我的Go语言第一课专栏在极客时间的7日飙升榜上跃升至第5名（截至2024.10.29 21点），估计这是借了双十一的光，也感谢大家的支持与厚爱！借此在这里给自己的专栏打个广告。 Go语言：简单直接 正如前面所提到的，Go语言的设计理念就是追求简单与直接。无论是基础语法还是并发编程，Go都让你在最短的时间内上手，效果立竿见影。你会发现，编写清晰、优雅的代码并不是一件难事。 Go社区的快速增长 今年，Go语言的社区发展迅猛，这在Reddit的Go分论坛上体现明显，每周都有超过1k名新会员加入。这不仅显示了Go语言的受欢迎程度，更证明了它在开发者中的广泛应用。 号召大家入门Go 如果你还在犹豫，不妨趁这个时机，加入Go的学习行列！无论你是编程新手还是经验丰富的开发者，Go都能为你打开新的大门。我的专栏将为你提供实用的学习资源和案例分析，助你快速入门。 一起踏上Go之旅！ 现在就扫码关注我的“Go语言第一课”专栏吧，让我们一起体验Go语言的魅力，享受编程的乐趣！期待在这个快速发展的社区中见到你的身影！ 读者精彩评论 以下是“写Go就像喝白开水”一文在公众号首发后一些读者的精彩评论的摘录： C是自来水，得烧开才能喝的安全，go是已经烧好的白开水，即时解渴，高效，c#，Java 是奶茶咖啡，看起来高级，但加了狠活，弄不好很难喝，喝完可能窜稀。 &#8212; 网友 Run C++是白酒喝高了上头 &#8212; 网友 ニコニコ boring but useful 形容go确实贴切 &#8212; 网友 领个废宅 Gopher部落知识星球在2024年将继续致力于打造一个高品质的Go语言学习和交流平台。我们将继续提供优质的Go技术文章首发和阅读体验。同时，我们也会加强代码质量和最佳实践的分享，包括如何编写简洁、可读、可测试的Go代码。此外，我们还会加强星友之间的交流和互动。欢迎大家踊跃提问，分享心得，讨论技术。我会在第一时间进行解答和交流。我衷心希望Gopher部落可以成为大家学习、进步、交流的港湾。让我相聚在Gopher部落，享受coding的快乐! 欢迎大家踊跃加入！ 著名云主机服务厂商DigitalOcean发布最新的主机计划，入门级Droplet配置升级为：1 core CPU、1G内存、25G高速SSD，价格5$/月。有使用DigitalOcean需求的朋友，可以打开这个链接地址：https://m.do.co/c/bff6eed92687 开启你的DO主机之路。 Gopher Daily(Gopher每日新闻) &#8211; https://gopherdaily.tonybai.com 我的联系方式： 微博(暂不可用)：https://weibo.com/bigwhite20xx 微博2：https://weibo.com/u/6484441286 博客：tonybai.com github: https://github.com/bigwhite Gopher Daily归档 &#8211; https://github.com/bigwhite/gopherdaily [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/go-coding-is-like-drinking-boiled-water-1.png" alt="" /></p>
<p><a href="https://tonybai.com/2024/10/29/go-coding-is-like-drinking-boiled-water">本文永久链接</a> &#8211; https://tonybai.com/2024/10/29/go-coding-is-like-drinking-boiled-water</p>
<p>在编程语言的世界里，Go语言简单而直接，它没有复杂的语法和华丽的特性，给人一种纯粹的感觉，让我们在编写代码时感受到了一种清晰和高效。</p>
<p>正如Russ Cox所言，<a href="https://tonybai.com/2022/12/29/the-2022-review-of-go-programming-language">Go的“无聊”</a>恰恰是它的优势。抛开冗余装饰，Go专注于可靠、实用的功能。在这个快节奏的时代，它让我们免受复杂性的困扰，帮助我们快速解决实际问题。</p>
<p><img src="https://tonybai.com/wp-content/uploads/the-2022-review-of-go-programming-language-3.png" alt="" /></p>
<p><strong>写Go就像喝一杯无味的白开水，虽寡淡却能即刻解渴，满足需求，并少有后顾之忧</strong>。平淡中透着从容，是我们日常开发中的可靠之选。</p>
<p><img src="https://tonybai.com/wp-content/uploads/go-coding-is-like-drinking-boiled-water-2.png" alt="" /></p>
<p>早上打开极客时间的首页，发现我的<a href="http://gk.link/a/10AVZ">Go语言第一课专栏</a>在极客时间的7日飙升榜上跃升至第5名（截至2024.10.29 21点），估计这是借了双十一的光，也感谢大家的支持与厚爱！借此在这里给自己的专栏打个广告。</p>
<p><img src="https://tonybai.com/wp-content/uploads/go-coding-is-like-drinking-boiled-water-3.png" alt="" /></p>
<p><strong>Go语言：简单直接</strong></p>
<p>正如前面所提到的，Go语言的设计理念就是追求简单与直接。无论是基础语法还是并发编程，Go都让你在最短的时间内上手，效果立竿见影。你会发现，编写清晰、优雅的代码并不是一件难事。</p>
<p><strong>Go社区的快速增长</strong></p>
<p>今年，Go语言的社区发展迅猛，这在<a href="https://www.reddit.com/r/golang/">Reddit的Go分论坛</a>上体现明显，每周都有超过1k名新会员加入。这不仅显示了Go语言的受欢迎程度，更证明了它在开发者中的广泛应用。</p>
<p><img src="https://tonybai.com/wp-content/uploads/go-coding-is-like-drinking-boiled-water-4.png" alt="" /></p>
<p><strong>号召大家入门Go</strong></p>
<p>如果你还在犹豫，不妨趁这个时机，加入Go的学习行列！无论你是编程新手还是经验丰富的开发者，Go都能为你打开新的大门。我的专栏将为你提供实用的学习资源和案例分析，助你快速入门。</p>
<p><strong>一起踏上Go之旅！</strong></p>
<p>现在就扫码关注我的“Go语言第一课”专栏吧，让我们一起体验Go语言的魅力，享受编程的乐趣！期待在这个快速发展的社区中见到你的身影！</p>
<p><img src="http://image.tonybai.com/img/tonybai/go-first-course-banner.png" alt="img{512x368}" /></p>
<p><strong>读者精彩评论</strong></p>
<p>以下是<a href="https://mp.weixin.qq.com/s/SQ28DOKPPLIpkD-Zlxskdw">“写Go就像喝白开水”一文在公众号首发后</a>一些读者的精彩评论的摘录：</p>
<blockquote>
<p>C是自来水，得烧开才能喝的安全，go是已经烧好的白开水，即时解渴，高效，c#，Java 是奶茶咖啡，看起来高级，但加了狠活，弄不好很难喝，喝完可能窜稀。 &#8212; 网友 Run</p>
<p>C++是白酒喝高了上头 &#8212; 网友 ニコニコ</p>
<p>boring but useful 形容go确实贴切 &#8212; 网友 领个废宅</p>
</blockquote>
<hr />
<p><a href="https://public.zsxq.com/groups/51284458844544">Gopher部落知识星球</a>在2024年将继续致力于打造一个高品质的Go语言学习和交流平台。我们将继续提供优质的Go技术文章首发和阅读体验。同时，我们也会加强代码质量和最佳实践的分享，包括如何编写简洁、可读、可测试的Go代码。此外，我们还会加强星友之间的交流和互动。欢迎大家踊跃提问，分享心得，讨论技术。我会在第一时间进行解答和交流。我衷心希望Gopher部落可以成为大家学习、进步、交流的港湾。让我相聚在Gopher部落，享受coding的快乐! 欢迎大家踊跃加入！</p>
<p><img src="http://image.tonybai.com/img/tonybai/gopher-tribe-zsxq-small-card.png" alt="img{512x368}" /><br />
<img src="http://image.tonybai.com/img/tonybai/go-programming-from-beginner-to-master-qr.png" alt="img{512x368}" /></p>
<p><img src="http://image.tonybai.com/img/tonybai/imooc-go-column-pgo-with-qr.jpg" alt="img{512x368}" /></p>
<p>著名云主机服务厂商DigitalOcean发布最新的主机计划，入门级Droplet配置升级为：1 core CPU、1G内存、25G高速SSD，价格5$/月。有使用DigitalOcean需求的朋友，可以打开这个<a href="https://m.do.co/c/bff6eed92687">链接地址</a>：https://m.do.co/c/bff6eed92687 开启你的DO主机之路。</p>
<p>Gopher Daily(Gopher每日新闻) &#8211; https://gopherdaily.tonybai.com</p>
<p>我的联系方式：</p>
<ul>
<li>微博(暂不可用)：https://weibo.com/bigwhite20xx</li>
<li>微博2：https://weibo.com/u/6484441286</li>
<li>博客：tonybai.com</li>
<li>github: https://github.com/bigwhite</li>
<li>Gopher Daily归档 &#8211; https://github.com/bigwhite/gopherdaily</li>
<li>Gopher Daily Feed订阅 &#8211; https://gopherdaily.tonybai.com/feed</li>
</ul>
<p><img src="http://image.tonybai.com/img/tonybai/iamtonybai-wechat-qr.png" alt="" /></p>
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。</p>
<p style='text-align:left'>&copy; 2024, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2024/10/29/go-coding-is-like-drinking-boiled-water/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>认知负荷对编程语言选择和学习的影响</title>
		<link>https://tonybai.com/2024/10/24/cognitive-load-impact-on-programming-language-choice-and-study/</link>
		<comments>https://tonybai.com/2024/10/24/cognitive-load-impact-on-programming-language-choice-and-study/#comments</comments>
		<pubDate>Wed, 23 Oct 2024 22:12:30 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[思考控]]></category>
		<category><![CDATA[技术志]]></category>
		<category><![CDATA[AI]]></category>
		<category><![CDATA[C]]></category>
		<category><![CDATA[cargo]]></category>
		<category><![CDATA[CLASS_PATH]]></category>
		<category><![CDATA[CognitiveLoad]]></category>
		<category><![CDATA[conan]]></category>
		<category><![CDATA[Cpp]]></category>
		<category><![CDATA[DesignPattern]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[go1.11]]></category>
		<category><![CDATA[gofmt]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[Go语言精进之路]]></category>
		<category><![CDATA[gradle]]></category>
		<category><![CDATA[IDE]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[JAVA_HOME]]></category>
		<category><![CDATA[Maven]]></category>
		<category><![CDATA[MentalLoad]]></category>
		<category><![CDATA[PEP8]]></category>
		<category><![CDATA[pip]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Rust]]></category>
		<category><![CDATA[rustfmt]]></category>
		<category><![CDATA[rustup]]></category>
		<category><![CDATA[TypeScript]]></category>
		<category><![CDATA[vcpkg]]></category>
		<category><![CDATA[三脑理论]]></category>
		<category><![CDATA[人工智能]]></category>
		<category><![CDATA[借用检查]]></category>
		<category><![CDATA[元编程]]></category>
		<category><![CDATA[内在认知]]></category>
		<category><![CDATA[内存管理]]></category>
		<category><![CDATA[函数式编程]]></category>
		<category><![CDATA[原始脑]]></category>
		<category><![CDATA[外在认知]]></category>
		<category><![CDATA[工具]]></category>
		<category><![CDATA[平台期]]></category>
		<category><![CDATA[并发]]></category>
		<category><![CDATA[开发环境]]></category>
		<category><![CDATA[心智负担]]></category>
		<category><![CDATA[思维]]></category>
		<category><![CDATA[思考，快与慢]]></category>
		<category><![CDATA[情绪脑]]></category>
		<category><![CDATA[所有权]]></category>
		<category><![CDATA[文档]]></category>
		<category><![CDATA[模板]]></category>
		<category><![CDATA[泛型]]></category>
		<category><![CDATA[爬虫脑]]></category>
		<category><![CDATA[理性脑]]></category>
		<category><![CDATA[相关认知]]></category>
		<category><![CDATA[认知觉醒]]></category>
		<category><![CDATA[认知负荷]]></category>
		<category><![CDATA[设计模式]]></category>
		<category><![CDATA[语法]]></category>
		<category><![CDATA[跨平台]]></category>
		<category><![CDATA[面向对象]]></category>
		<category><![CDATA[面向过程]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=4353</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2024/10/24/cognitive-load-impact-on-programming-language-choice-and-study 在《Go语言精进之路：从新手到高手的编程思想、方法和技巧》两卷书出版后，我收到了一些读者的反馈。其中一位读者提到：“为什么作者如此偏爱使用心智负担这个词？”当时我对此并未给予太多关注。然而，近期我阅读了一些关于认知心理学和脑科学的著作后，才意识到读者的反馈不仅仅是对该词频繁使用的关注，更可能暗示了用词不当的问题。 “心智负担”（Mental Load）指的是在处理多任务或日常生活安排时所需耗费的心理资源和精力，包括记忆、计划、组织以及应对各种任务所带来的精神压力。然而，在学习、思考和理解的情境中，特别是在编程语言的学习中，使用“认知负荷”（Cognitive Load）这一术语可能更为恰当。 认知负荷理论最初由澳大利亚新南威尔士大学的认知心理学家约翰·斯威勒(John Sweller)于1988年首先提出来的，旨在解释学习过程中的认知资源分配。认知负荷是指在学习、思考或解决问题时，大脑在处理信息和执行任务时所承受的负担。在选择编程语言时，认知负荷是一个至关重要的因素，指的是人们在学习和使用某种编程语言时，为理解语法、掌握工具和解决问题所需付出的心理负担和精力。 那么，在面对众多主流编程语言时，在不考虑市场需求与公司或组织强制学习的情况下，认知负荷究竟如何影响开发人员对编程语言的选择呢？在这篇文章中，我将进行一些不那么严谨，也非专业的粗略探讨，希望能够为大家带来一些启发。 1. 认知负荷在编程语言中的体现 认知负荷理论发展到今天，其总体被分为三种类型： 内在认知负荷(Intrinsic Cognitive Load) 内在认知负荷，也称为固有负荷，是由学习材料本身的复杂性所决定的，它与学习任务的本质和内容密切相关。例如，编程语言的语法规则、数据类型、内存管理和并发模型等都是内在负荷的一部分。学习这些概念的难易程度主要取决于编程语言本身的设计和复杂度。 外在认知负荷(Extraneous Cognitive Load) 外在负荷是由学习环境和教学方式引起的负担，通常是由于无关信息或低效的学习方法造成的。比如，配置开发环境、学习非必要的工具或被复杂的IDE界面困扰，都可能增加外在负荷。在编程语言学习中，清晰的文档和易于理解的教程可以显著减少外在负荷。 虽然外在负荷不是由编程语言语法本身决定的，但它会影响新手的学习体验。如果学习资源和工具太复杂或不直观，即使是简单的编程语言也会让人感到困难。 相关认知负荷(Germane Cognitive Load) 相关认知负荷是指学习过程中专门用于理解、整合和构建知识结构的认知努力。它与思维加工、模式识别、知识内化等过程有关。在编程中，相关认知负荷指的是学习者在掌握编程思想、设计模式和编程习惯时所付出的努力。例如，理解如何在实际项目中应用编程概念，如何优化代码设计，以及如何解决编程中的复杂问题，这些过程都会增加相关认知负荷。这种负担是积极的，因为它有助于深入理解和长期记忆。 下面这张图来自网络，可以帮助我们进一步理解三类认知负荷(只是出发点来自教学角度)： 由此可见，对于新手来说，学习一门编程语言时，外在认知负荷是第一道门槛，它决定了是否能坚持学习，还是选择“Hello and Bye”；内在认知负荷则是基础，是核心；相关认知负荷则是进阶挑战，决定了可以达到的高度。 接下来，我们将针对一些主流编程语言，沿着新手入门学习编程语言的认知负荷先后顺序进行粗略对比。希望这能为大家提供在编程语言选择方面的有用信息，同时帮助不同阶段的学习者针对各自的认知负荷水平做好心理准备。 2. 主流编程语言的认知负荷对比 在探讨主流编程语言的认知负荷时，我们需要从外在认知负荷、内在认知负荷以及相关认知负荷这三个维度进行深入分析。这种分析不仅能帮助我们理解不同语言的特点，更能为选择合适的编程语言提供参考依据。 注：笔者是后端程序员出身，对前端语言比如Javascript、Typescript等了解有限，因此这里将使用像Go、Rust、C++等主流后端语言作为分析和对比的参考对象。 2.1 外在认知负荷的影响 在编程语言学习的初始阶段，外在认知负荷往往是最先遇到的挑战。 Python在这方面表现出色，它简单的环境搭建流程让初学者能够快速开始编程之旅。只需安装一个解释器，新手就能立即开始编写代码。虽然在使用pip管理依赖时可能遇到一些包冲突的问题，但整体来说，在环境搭建、工具使用等外在认知负荷方面对初学者相当友好。 Go语言同样提供了令人称道的开发体验。它的工具链安装过程直观明了，跨平台支持也十分完善。特别值得一提的是，自从Go 1.11引入go modules以来，依赖管理变得更加自动化和直观。虽然对新手来说，理解版本控制可能需要一些时间。此外，Go团队也给出了Go项目布局的官方建议，为开发者进行代码组织提供了清晰的参考。 相比之下，C++的环境搭建则显得较为复杂。开发者需要安装编译器，配置IDE，这些步骤对新手来说都构成了不小的挑战。加上缺乏统一的包管理工具（尽管vcpkg和conan等工具正在改变这一现状），以及灵活但缺乏标准的项目结构，都让C++的外在认知负荷明显高于其他语言。 Rust通过其官方工具链安装工具rustup提供了相对简便的环境搭建方式。它的Cargo包管理器集成度高，使用便捷，而且项目结构的标准化程度高，这些特点都有效降低了外在认知负荷。 Java则介于两个极端之间。它需要安装JDK并配置环境变量(如JAVA_HOME、CLASS_PATH等)，这个过程对新手来说可能有些繁琐。虽然Maven和Gradle这样的依赖管理工具功能强大，但学习曲线较陡峭。不过，Java严格的项目布局规范在初期可能显得死板，但从长远来看反而有助于培养良好的工程习惯。 过了环境安装、工具使用和项目布局这些“外在认知负荷”的关卡后，语言自身的复杂性便会成为新手面前的更大的挑战。 2.2 内在认知负荷考量 谈到语言本身的复杂性，Python的设计理念“简单胜于复杂”使其成为认知负荷最低的选择之一。它的语法接近自然语言，几乎不需要特别的学习就能读懂基本的代码结构。这种简洁性使得Python特别适合编程初学者，以至于主流的儿童编程教学大多使用Python(当然一些启蒙教学使用的是scratch)。 Go语言同样以简洁著称，它的语法设计注重一致性和可读性。虽然保留了指针这样的底层特性，可能会让某些初学者感到困惑，但整体而言，Go的学习曲线相当平缓。值得注意的是，Go 1.18引入泛型后，虽然提升了语言的表达能力，但也增加了一定的复杂性。至于Go是否适合作为从零开始编程的新手，也是见仁见智。 C++的内在认知负荷则明显较高。它支持多种编程范式，包括面向过程、面向对象、模板编程等，这些范式和特性固然强大，但对初学者来说往往构成了较大的认知负担。特别是在处理多态、模板元编程等高级特性时，学习曲线会变得异常陡峭。 Rust的内在认知负荷同样不低，但事实证明其复杂性是有意义的。它的所有权系统和借用检查器虽然增加了学习难度，但这些机制对于理解系统编程的本质非常有帮助，同时提高了程序在运行时的安全性。新手在最初接触这些概念时可能会感到困惑，但掌握后会对内存安全有深刻的理解。 Java的内在认知负荷介于中等水平。它的面向对象语法虽然比Python或Go略显繁琐，但整体而言还算直观。Java的复杂性主要体现在面向对象设计模式、泛型和异常处理等特性上，这些概念需要时间来消化和掌握。 2.3 [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/cognitive-load-impact-on-programming-language-choice-and-study-1.png" alt="" /></p>
<p><a href="https://tonybai.com/2024/10/24/cognitive-load-impact-on-programming-language-choice-and-study">本文永久链接</a> &#8211; https://tonybai.com/2024/10/24/cognitive-load-impact-on-programming-language-choice-and-study</p>
<p>在《<a href="https://item.jd.com/13694000.html">Go语言精进之路：从新手到高手的编程思想、方法和技巧</a>》两卷书出版后，我收到了一些读者的反馈。其中一位读者提到：“为什么作者如此偏爱使用<code>心智负担</code>这个词？”当时我对此并未给予太多关注。然而，近期我阅读了一些关于认知心理学和脑科学的著作后，才意识到读者的反馈不仅仅是对该词频繁使用的关注，更可能暗示了用词不当的问题。</p>
<p>“心智负担”（Mental Load）指的是在处理多任务或日常生活安排时所需耗费的心理资源和精力，包括记忆、计划、组织以及应对各种任务所带来的精神压力。然而，在学习、思考和理解的情境中，特别是在编程语言的学习中，使用“认知负荷”（Cognitive Load）这一术语可能更为恰当。</p>
<p>认知负荷理论最初由澳大利亚新南威尔士大学的认知心理学家约翰·斯威勒(John Sweller)于1988年首先提出来的，旨在解释学习过程中的认知资源分配。认知负荷是指在学习、思考或解决问题时，大脑在处理信息和执行任务时所承受的负担。在选择编程语言时，认知负荷是一个至关重要的因素，指的是人们在学习和使用某种编程语言时，为理解语法、掌握工具和解决问题所需付出的心理负担和精力。</p>
<p>那么，在面对众多主流编程语言时，在<strong>不考虑市场需求与公司或组织强制学习的情况</strong>下，认知负荷究竟如何影响开发人员对编程语言的选择呢？在这篇文章中，我将进行一些不那么严谨，也非专业的粗略探讨，希望能够为大家带来一些启发。</p>
<h2>1. 认知负荷在编程语言中的体现</h2>
<p>认知负荷理论发展到今天，其总体被分为三种类型：</p>
<ul>
<li>内在认知负荷(Intrinsic Cognitive Load)</li>
</ul>
<p>内在认知负荷，也称为固有负荷，是由学习材料本身的复杂性所决定的，它与学习任务的本质和内容密切相关。例如，编程语言的语法规则、<a href="https://tonybai.com/2022/12/18/go-type-system">数据类型</a>、<a href="https://tonybai.com/2023/06/13/understand-go-gc-overhead-behind-the-convenience">内存管理</a>和<a href="https://tonybai.com/2015/06/23/concurrency-and-parallelism/">并发</a>模型等都是内在负荷的一部分。学习这些概念的难易程度主要取决于编程语言本身的设计和复杂度。</p>
<ul>
<li>外在认知负荷(Extraneous Cognitive Load)</li>
</ul>
<p>外在负荷是由学习环境和教学方式引起的负担，通常是由于无关信息或低效的学习方法造成的。比如，配置开发环境、学习非必要的工具或被复杂的IDE界面困扰，都可能增加外在负荷。在编程语言学习中，<a href="https://tonybai.com/2023/03/20/godoc-vs-go-doc-vs-pkgsite">清晰的文档</a>和易于理解的教程可以显著减少外在负荷。</p>
<p>虽然外在负荷不是由编程语言语法本身决定的，但它会影响新手的学习体验。如果学习资源和工具太复杂或不直观，即使是简单的编程语言也会让人感到困难。</p>
<ul>
<li>相关认知负荷(Germane Cognitive Load)</li>
</ul>
<p>相关认知负荷是指学习过程中专门用于理解、整合和构建知识结构的认知努力。它与思维加工、模式识别、知识内化等过程有关。在编程中，相关认知负荷指的是学习者在掌握编程思想、设计模式和编程习惯时所付出的努力。例如，理解如何在实际项目中应用编程概念，如何优化代码设计，以及如何解决编程中的复杂问题，这些过程都会增加相关认知负荷。这种负担是积极的，因为它有助于深入理解和长期记忆。</p>
<p>下面这张图来自网络，可以帮助我们进一步理解三类认知负荷(只是出发点来自教学角度)：</p>
<p><img src="https://tonybai.com/wp-content/uploads/cognitive-load-impact-on-programming-language-choice-and-study-2.png" alt="" /></p>
<p>由此可见，对于新手来说，学习一门编程语言时，<strong>外在认知负荷是第一道门槛，它决定了是否能坚持学习，还是选择“Hello and Bye”；内在认知负荷则是基础，是核心；相关认知负荷则是进阶挑战，决定了可以达到的高度</strong>。</p>
<p>接下来，我们将针对一些主流编程语言，<strong>沿着新手入门学习编程语言的认知负荷先后顺序进行粗略对比</strong>。希望这能为大家提供在编程语言选择方面的有用信息，同时帮助不同阶段的学习者针对各自的认知负荷水平做好心理准备。</p>
<h2>2. 主流编程语言的认知负荷对比</h2>
<p>在探讨主流编程语言的认知负荷时，我们需要从外在认知负荷、内在认知负荷以及相关认知负荷这三个维度进行深入分析。这种分析不仅能帮助我们理解不同语言的特点，更能为选择合适的编程语言提供参考依据。</p>
<blockquote>
<p>注：笔者是后端程序员出身，对前端语言比如Javascript、Typescript等了解有限，因此这里将使用像Go、Rust、C++等主流后端语言作为分析和对比的参考对象。</p>
</blockquote>
<h3>2.1 外在认知负荷的影响</h3>
<p>在编程语言学习的初始阶段，<strong>外在认知负荷往往是最先遇到的挑战</strong>。</p>
<p>Python在这方面表现出色，它简单的环境搭建流程让初学者能够快速开始编程之旅。只需安装一个解释器，新手就能立即开始编写代码。虽然在使用pip管理依赖时可能遇到一些包冲突的问题，但整体来说，在环境搭建、工具使用等外在认知负荷方面对初学者相当友好。</p>
<p>Go语言同样提供了令人称道的开发体验。它的工具链安装过程直观明了，<a href="https://tonybai.com/2014/10/20/cross-compilation-with-golang/">跨平台支持</a>也十分完善。特别值得一提的是，自从<a href="https://tonybai.com/2018/11/19/some-changes-in-go-1-11/">Go 1.11</a>引入<a href="https://tonybai.com/tag/gomodule">go modules</a>以来，依赖管理变得更加自动化和直观。虽然对新手来说，理解版本控制可能需要一些时间。此外，Go团队也给出了<a href="https://tonybai.com/2023/10/05/the-official-guide-of-organizing-go-project">Go项目布局的官方建议</a>，为开发者进行代码组织提供了清晰的参考。</p>
<p>相比之下，C++的环境搭建则显得较为复杂。开发者需要安装编译器，配置IDE，这些步骤对新手来说都构成了不小的挑战。加上缺乏统一的包管理工具（尽管<a href="https://vcpkg.io/">vcpkg</a>和<a href="https://conan.io/">conan</a>等工具正在改变这一现状），以及灵活但缺乏标准的项目结构，都让C++的外在认知负荷明显高于其他语言。</p>
<p>Rust通过其官方工具链安装工具rustup提供了<a href="https://tonybai.com/2024/05/10/gopher-rust-first-lesson-setup-dev-env/">相对简便的环境搭建方式</a>。它的Cargo包管理器集成度高，使用便捷，而且项目结构的标准化程度高，这些特点都有效降低了外在认知负荷。</p>
<p>Java则介于两个极端之间。它需要安装JDK并配置环境变量(如JAVA_HOME、CLASS_PATH等)，这个过程对新手来说可能有些繁琐。虽然Maven和Gradle这样的依赖管理工具功能强大，但学习曲线较陡峭。不过，Java严格的项目布局规范在初期可能显得死板，但从长远来看反而有助于培养良好的工程习惯。</p>
<p>过了环境安装、工具使用和项目布局这些“外在认知负荷”的关卡后，语言自身的复杂性便会成为新手面前的更大的挑战。</p>
<h3>2.2 内在认知负荷考量</h3>
<p>谈到语言本身的复杂性，Python的设计理念“简单胜于复杂”使其成为认知负荷最低的选择之一。它的语法接近自然语言，几乎不需要特别的学习就能读懂基本的代码结构。这种简洁性使得Python特别适合编程初学者，以至于主流的儿童编程教学大多使用Python(当然一些启蒙教学使用的是scratch)。</p>
<p>Go语言同样<strong>以简洁著称</strong>，它的语法设计注重一致性和可读性。虽然保留了指针这样的底层特性，可能会让某些初学者感到困惑，但整体而言，Go的学习曲线相当平缓。值得注意的是，<a href="https://tonybai.com/2022/04/20/some-changes-in-go-1-18">Go 1.18引入泛型</a>后，虽然提升了语言的表达能力，但也增加了一定的复杂性。至于<a href="https://tonybai.com/2024/08/22/go-as-first-language">Go是否适合作为从零开始编程的新手</a>，也是见仁见智。</p>
<p>C++的内在认知负荷则明显较高。它支持多种编程范式，包括面向过程、面向对象、模板编程等，这些范式和特性固然强大，但对初学者来说往往构成了较大的认知负担。特别是在处理多态、模板元编程等高级特性时，学习曲线会变得异常陡峭。</p>
<p>Rust的内在认知负荷同样不低，但事实证明其复杂性是有意义的。它的所有权系统和借用检查器虽然增加了学习难度，但这些机制对于理解系统编程的本质非常有帮助，同时提高了程序在运行时的安全性。新手在最初接触这些概念时可能会感到困惑，但掌握后会对内存安全有深刻的理解。</p>
<p>Java的内在认知负荷介于中等水平。它的面向对象语法虽然比Python或Go略显繁琐，但整体而言还算直观。Java的复杂性主要体现在面向对象设计模式、泛型和异常处理等特性上，这些概念需要时间来消化和掌握。</p>
<h3>2.3 相关认知负荷的深入分析</h3>
<p>在实际应用知识解决问题时，各种语言呈现出不同的特点。</p>
<p>Python的优势在于它能让学习者快速将知识付诸实践。其丰富的标准库和生态、简洁的语法使得从学习到应用的过程异常顺畅。无论是数据科学还是Web开发，Python都能让新手快速看到成果。它支持多种编程范式，并且社区的<a href="https://peps.python.org/pep-0008/">PEP 8规范</a>为代码风格提供了清晰的指导。</p>
<p>Go语言在知识应用方面同样表现出色。它的工具链完善，容易将所学付诸实践。特别是在服务器端开发领域，Go的并发模型和简洁的语法让新手能够相对轻松地构建高效的后端服务。虽然Go不像传统的面向对象语言那样依赖继承体系，但其接口机制和组合方式为代码设计提供了优雅的解决方案。</p>
<p>C++的相关认知负荷较高，主要体现在将理论知识转化为实践时面临的挑战。内存管理和性能优化这些概念需要大量实践才能真正掌握。它支持多种编程范式，这种灵活性虽然强大，但对初学者来说往往是一把双刃剑。由于缺乏统一的编码规范，新手可能在选择最佳实践时感到困惑。</p>
<p>Rust在这方面呈现出独特的特点。它的所有权系统要求开发者在实践中深入思考内存管理问题，这个过程虽然充满挑战，但却能培养扎实的系统编程思维。Rust社区提供的编码规范和工具链都很完善，有助于形成良好的编程习惯。</p>
<p>Java则以其企业级开发的特点著称。它要求开发者深入理解面向对象编程的核心概念，这个过程需要较长时间的积累。Java的设计模式体系完备，社区的编码规范成熟，这些特点有助于培养专业的工程思维，但对新手来说可能需要更多的时间和耐心。</p>
<h3>2.4 综合评估</h3>
<p>通过以上分析，我们可以看出不同语言在认知负荷方面的特点。</p>
<p>Python以其全方位的低认知负荷成为初学者的理想选择。</p>
<p>Go语言通过简洁的设计和完善的工具链在降低认知负荷方面做出了显著成效。</p>
<p>Java虽然相对繁琐，但其成熟的生态系统和规范的开发流程为长期发展提供了良好基础。</p>
<p>Rust和C++的学习曲线较陡，但它们在系统编程和性能优化方面的深度让投入的学习成本变得有价值。</p>
<p>在理解了编程语言的认知负荷特点后，我们不妨再从心理学的角度，特别是借助三脑理论的视角，来探讨初学者是如何在面对不同编程语言时做出选择的。</p>
<h2>3. 初学者的编程语言学习决策过程</h2>
<p><a href="https://en.wikipedia.org/wiki/Triune_brain">三脑理论(Triune Brain Theory)</a>由Paul D. MacLean于1970年提出的理论假说，该理论将人脑分为三个层次，如下图所示：</p>
<p><img src="https://tonybai.com/wp-content/uploads/cognitive-load-impact-on-programming-language-choice-and-study-3.png" alt="" /><br />
<center>来自维基百科</center></p>
<ul>
<li>爬虫脑（Reptilian Brain）：也称原始脑，负责基本生存反应，包括对威胁的快速反应和本能行为。</li>
<li>情绪脑（Limbic System）：处理情绪和动机，影响记忆形成和社交行为。</li>
<li>理性脑（Neocortex）：负责高级认知功能，如逻辑思考、语言处理和复杂决策。</li>
</ul>
<blockquote>
<p>注：三脑理论提出较早，如今有新的理论认为三脑理论毫无依据。不过这里我们假定这个理论是正确和适用的。</p>
</blockquote>
<p>三脑理论影响初学者的编程学习决策的过程是怎样的呢？这个过程往往涉及本能反应(爬虫脑主导)、情感体验（情绪脑主导）和理性思考(理性脑主导)三个层面的互动。我们继续往下看。</p>
<h3>3.1 初学阶段的决策历程</h3>
<p>在首次接触编程语言时，学习者的反应往往是多层次的。本能层面的反应最为直接，面对像C++这样认知负荷较高的语言时，很多人会本能地产生畏惧感。这种反应不是简单的怯懦，而是大脑对复杂性的自然防御机制。相反，Python这类认知负荷较低的语言则较少触发这种应激反应，使得学习者能够保持相对轻松的心态。</p>
<p>情感层面的体验则更为复杂。当成功运行第一个程序时，无论使用什么语言，都会带来成就感。但随着学习的深入，不同语言带来的情感体验会产生分化。举个例子，我在早期学习Java时，仅仅是配置环境变量这样的基础工作就带来了挫折感，这种负面情绪很容易影响学习的积极性。而Rust虽然入门门槛较低，但一旦进入到所有权系统的学习，很多人会因为频繁的编译错误而感到沮丧。</p>
<p>理性思考则是决策过程中最后但也是最重要的环节。这包括对语言应用领域的评估、职业发展前景的考虑，以及个人学习时间和精力投入的权衡。这个阶段的决策通常更加慎重，也更具有长期性。</p>
<h3>3.2 深入学习阶段的转变</h3>
<p>随着学习的深入，最初的决策依据往往会发生改变。原本令人望而生畏的特性可能转变为吸引力的来源。这种转变在Rust的学习过程中特别明显，当开发者逐渐理解了所有权系统的价值，<strong>最初的困惑可能转化为对语言设计的欣赏</strong>。</p>
<p>在这个阶段，情感体验也往往变得更加丰富。<strong>克服困难带来的成就感可能超越了简单的编程快感</strong>，这也解释了为什么一些看似“难学”的语言反而能够培养出更加忠实的用户群体。Rust连续多年在最受欢迎编程语言榜单上位居前列，很大程度上就<strong>源于这种深层的技术认同感</strong>。</p>
<p>理性思考在这个阶段会更加全面，不再局限于语言本身的特性，而是扩展到整个技术生态系统的考量。开发者会更多地思考语言的性能特点、社区活跃度、工具链完善程度等因素。</p>
<h3>3.3 认知负荷与学习效果</h3>
<p>从短期来看，低认知负荷的语言确实能够提供更平缓的学习曲线，让入门过程更加顺畅。Python和Go在这方面的优势明显，它们能让学习者快速进入实践阶段，建立信心。但这种便利性有时也会带来一个意想不到的问题：<strong>学习者可能在掌握了基础语法后陷入平台期</strong>，难以实现质的突破。这也是<strong>为什么经常有读者询问如何才能在Go语言编程中更进一步</strong>。</p>
<p>相比之下，高认知负荷的语言虽然入门较难，但往往能够培养更深入的编程思维。比如Rust的所有权系统，虽然增加了学习难度，但这种设计迫使开发者深入思考内存管理的问题，从而建立更扎实的系统编程基础。C++的模板元编程虽然复杂，但掌握后能够大大提升代码的抽象能力和复用效率。</p>
<p>不过，我们也要警惕过高的认知负荷带来的风险。如果学习过程中的挫折感持续累积，很容易导致半途而废。每年<a href="https://tonybai.com/2024/04/22/gopher-rust-first-lesson-all-about-rust">入门一次Rust</a>的真实案例也屡见不鲜。这就要求我们在选择编程语言时，既要考虑个人的学习能力和时间投入，也要权衡职业发展的需求，找到一个适合自己的平衡点。</p>
<h2>4. 小结</h2>
<p>在探讨了认知负荷对编程语言学习的影响后，我们可以得出一些粗浅的见解：编程语言的学习绝非简单的语法掌握过程，而是一个涉及多个认知维度的复杂历程。从开发环境的搭建到语言特性的理解，从基础概念的掌握到工程实践的应用，每个阶段都会给学习者带来不同程度的认知压力。理解这些认知负荷的本质，有助于我们做出更明智的编程语言学习的选择。</p>
<p>对于编程新手来说，像Python和Go这样在各个维度都尽量降低认知负荷的语言，无疑是入门的理想选择。但我们也要认识到，较高的认知负荷未必就是缺点。就像Rust和C++这样的语言，它们的学习曲线虽然陡峭，但这种”困难”往往蕴含着宝贵的学习机会。通过克服这些认知挑战，开发者能够建立起更深入的系统编程认知，形成更扎实的技术功底。</p>
<p>选择合适的编程语言，某种程度上就像选择一位长期相处的伙伴。这个选择不仅要考虑语言本身的特点，还要权衡个人的学习能力、职业规划和时间投入。<strong>认知负荷理论为我们提供了一个有价值的分析框架</strong>，但最终的选择还是要回归到个人的实际需求和发展目标。正如没有完美的编程语言一样，也没有放之四海而皆准的学习路径。找到适合自己的平衡点，或许才是最务实的学习策略。</p>
<p>最后，在<a href="https://tonybai.com/2024/10/14/programming-in-ai-era/">人工智能编码辅助技术飞速发展</a>的今天，开放的学习心态和持续学习的能力，可能比选择某个特定的编程语言更为重要。毕竟唯一不变的可能就是变化本身。</p>
<h2>5. 参考资料</h2>
<ul>
<li>《<a href="https://book.douban.com/subject/10785583/">思考，快与慢</a>》- https://book.douban.com/subject/10785583/</li>
<li>《<a href="https://book.douban.com/subject/35193035/">认知觉醒</a>》 &#8211; https://book.douban.com/subject/35193035/</li>
</ul>
<hr />
<p><a href="https://public.zsxq.com/groups/51284458844544">Gopher部落知识星球</a>在2024年将继续致力于打造一个高品质的Go语言学习和交流平台。我们将继续提供优质的Go技术文章首发和阅读体验。同时，我们也会加强代码质量和最佳实践的分享，包括如何编写简洁、可读、可测试的Go代码。此外，我们还会加强星友之间的交流和互动。欢迎大家踊跃提问，分享心得，讨论技术。我会在第一时间进行解答和交流。我衷心希望Gopher部落可以成为大家学习、进步、交流的港湾。让我相聚在Gopher部落，享受coding的快乐! 欢迎大家踊跃加入！</p>
<p><img src="http://image.tonybai.com/img/tonybai/gopher-tribe-zsxq-small-card.png" alt="img{512x368}" /><br />
<img src="http://image.tonybai.com/img/tonybai/go-programming-from-beginner-to-master-qr.png" alt="img{512x368}" /></p>
<p><img src="http://image.tonybai.com/img/tonybai/go-first-course-banner.png" alt="img{512x368}" /><br />
<img src="http://image.tonybai.com/img/tonybai/imooc-go-column-pgo-with-qr.jpg" alt="img{512x368}" /></p>
<p>著名云主机服务厂商DigitalOcean发布最新的主机计划，入门级Droplet配置升级为：1 core CPU、1G内存、25G高速SSD，价格5$/月。有使用DigitalOcean需求的朋友，可以打开这个<a href="https://m.do.co/c/bff6eed92687">链接地址</a>：https://m.do.co/c/bff6eed92687 开启你的DO主机之路。</p>
<p>Gopher Daily(Gopher每日新闻) &#8211; https://gopherdaily.tonybai.com</p>
<p>我的联系方式：</p>
<ul>
<li>微博(暂不可用)：https://weibo.com/bigwhite20xx</li>
<li>微博2：https://weibo.com/u/6484441286</li>
<li>博客：tonybai.com</li>
<li>github: https://github.com/bigwhite</li>
<li>Gopher Daily归档 &#8211; https://github.com/bigwhite/gopherdaily</li>
<li>Gopher Daily Feed订阅 &#8211; https://gopherdaily.tonybai.com/feed</li>
</ul>
<p><img src="http://image.tonybai.com/img/tonybai/iamtonybai-wechat-qr.png" alt="" /></p>
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。</p>
<p style='text-align:left'>&copy; 2024, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2024/10/24/cognitive-load-impact-on-programming-language-choice-and-study/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>2023年的Rust与Go[译]</title>
		<link>https://tonybai.com/2023/02/22/rust-vs-go-in-2023/</link>
		<comments>https://tonybai.com/2023/02/22/rust-vs-go-in-2023/#comments</comments>
		<pubDate>Wed, 22 Feb 2023 13:49:14 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[benchmark]]></category>
		<category><![CDATA[Channel]]></category>
		<category><![CDATA[Compile]]></category>
		<category><![CDATA[Cpp]]></category>
		<category><![CDATA[DevOps]]></category>
		<category><![CDATA[GC]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[gofmt]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[goroutine]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[Mutex]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[RobPike]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Rust]]></category>
		<category><![CDATA[rustfmt]]></category>
		<category><![CDATA[scale]]></category>
		<category><![CDATA[内存安全]]></category>
		<category><![CDATA[垃圾回收]]></category>
		<category><![CDATA[基准测试]]></category>
		<category><![CDATA[多范式]]></category>
		<category><![CDATA[安全]]></category>
		<category><![CDATA[并发]]></category>
		<category><![CDATA[微服务]]></category>
		<category><![CDATA[性能]]></category>
		<category><![CDATA[抽象]]></category>
		<category><![CDATA[机器]]></category>
		<category><![CDATA[类型安全]]></category>
		<category><![CDATA[系统编程]]></category>
		<category><![CDATA[线程]]></category>
		<category><![CDATA[编程]]></category>
		<category><![CDATA[编译]]></category>
		<category><![CDATA[编译型语言]]></category>
		<category><![CDATA[语法]]></category>
		<category><![CDATA[通用语言]]></category>
		<category><![CDATA[静态语言]]></category>
		<category><![CDATA[面向对象]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=3804</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2023/02/22/rust-vs-go-in-2023 本文译自《Rust vs Go in 2023》。 注：从2022年下半年开始，我们研发团队的产品研发不再局限于云端，车端也是将来的一个重要方向。于是我除了继续对Go语言保持常规的高度关注之外，也逐步开始留意Rust语言的发展。 Rust和Go哪个更好？Go还是Rust？在2023年，你应该为你的下一个项目选择哪种语言，为什么？两者在性能、简单性、安全性、功能、规模和并发性等方面如何比较？它们的共同点是什么，它们有哪些根本性的不同？让我们在这个友好而公平的Rust和Go的比较中找到答案。 Rust和Go都很棒 首先，我必须要说的是，Go和Rust都是绝对优秀的编程语言。它们都是现代的、强大的、被广泛采用的编程语言，并且都提供出色的性能。 你可能读过一些说Go比Rust好的文章，或者相反。但这真的没有意义；每一种编程语言都代表了一系列的权衡和取舍。每种语言都有自己的优化重点，所以你对语言的选择应该由适合你的东西和你想用它解决的问题决定。 在这篇文章中，我将尝试告诉你何时使用Go是理想选择以及何时使用Rust更佳。我也会试着介绍一下这两种语言的本质（如果你愿意的话，就是Go和Rust的道）。 虽然它们在语法和风格上有很大不同，但Rust和Go都是构建软件的一流工具。接下来，让我们仔细看看这两种语言。 Go和Rust的相似之处 Rust和Go有很多共同点，这也是你经常听到它们一起被提及的原因之一。两种语言的共同目标是什么呢？ Rust是一种低级静态类型的多范式编程语言，专注于安全和性能。 &#8211; Gints Dreimanis Go是一种开源的编程语言，可以轻松构建简单、可靠、高效的软件。 &#8211; go.dev 内存安全 Go和Rust都属于现代编程语言，它们的首要任务是内存安全。经过几十年对C和C++等旧语言的使用，我们可以清楚地看到，导致错误和安全漏洞的最大原因之一是不安全地或不正确地访问内存。 Rust和Go以不同的方式处理这个问题，但它们的目标都是在管理内存方面比其他语言更聪明、更安全，并帮助你写出正确和高性能的程序。 快速、紧凑的可执行文件 Go和Rust都是编译型语言，这意味着你的程序被直接翻译成可执行的机器码，因此你可以以单一二进制文件形式来部署你的程序；与Python和Ruby等解释型语言不同，你不需要将解释器和大量的库和依赖关系与你的程序一起分发，这是一个很大的优点。这也使得Rust和Go的程序与解释型语言相比都非常快。 通用语言 Rust和Go都是强大的、可扩展的通用编程语言，你可以用它们来开发各种现代软件，从网络应用到分布式微服务，或者从嵌入式微控制器到移动应用程序。 两者都有优秀的标准库、繁荣的第三方生态系统以及巨大的商业支持和庞大的用户基础。它们都已经存在了很多年，并将在未来几年内继续被广泛使用。今天学习Go或Rust将是对你时间和精力的合理投资。 务实的编程风格 Go和Rust都不是以函数式编程为主的语言（例如像Scala或Elixir），也不是完全面向对象的语言（像Java和C#）。相反，虽然Go和Rust都有与函数式和面向对象编程相关的特性，但它们是务实的语言，旨在以最合适的方式解决问题，而不是强迫你采用特定的做事方式。 如果你喜欢函数式编程风格，你会在Rust中发现更多对这种风格的支持，因为Rust在语法特性数量上要比Go更多。 我们可以讨论什么是“面向对象”语言，但可以说C++、Java或C#用户所期望的面向对象编程风格在Go或Rust中都不存在。 &#8211; Jack Mott 规模化的开发 Rust和Go都有一些有用的特性，使它们适合于大规模的编程，不管是指大型团队，还是大型代码库，或者两者兼具。 例如，C语言的程序员们多年来一直在争论将括号放在哪里，以及代码应该用制表符还是空格缩进，而Rust和Go通过使用标准的格式化工具（Go为gofmt，Rust为rustfmt）使用规范的风格自动重写你的代码，完全消除了这些问题。 这并不是说这种特殊的风格本身有多好：而是Rust和Go的程序员都喜欢这种标准化。 gofmt的风格是没有人喜欢的，但gofmt却是所有人的最爱。 &#8211; Rob Pike 两种语言的另一个高分领域是构建管道(pipeline)。两种语言都有优秀的、内置的、高性能的标准构建和依赖管理工具；不再需要与复杂的第三方构建系统搏斗，也不再需要每隔几年就学习一个新的系统。 对于早期职业生涯以Java和Ruby为背景的我而言，构建Go和Rust代码感觉就像从我的肩上卸下了一个不可能的重担。当我在谷歌工作时，遇到用Go编写的服务是一种解脱，因为我知道它很容易构建和运行。Rust也是如此，尽管我只在较小规模的Rust项目上工作过。我希望可无限配置的构建系统的时代已经过去了，所有语言都会有自己专门的构建工具，开箱即可使用。- 山姆-罗斯 Rust还是Go？ 综上可知，这两种语言都设计得很好、很强大，那么你可能会想知道那些关于两门语言的“圣战”究竟是怎么回事（我也是）。为什么人们对“Go vs.Rust”如此大惊小怪，在社交媒体上大打出手，并且写长篇博文说只有傻瓜才会使用Rust，或者Go不是真正的编程语言，或者其他什么。 这可能会让他们感觉好些，但这并不能完全帮助你，因为你正试图决定在你的项目中使用哪种语言，或者你应该学习哪种语言来推动你的编程生涯。一个明智的人不会根据谁喊得声最大来做出重要的选择。 现在让我们继续我们成熟的讨论，看看在某些领域，一个有理智的人可能更喜欢哪一种语言。 [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/rust-vs-go-in-2023-1.png" alt="" /></p>
<p><a href="https://tonybai.com/2023/02/22/rust-vs-go-in-2023">本文永久链接</a> &#8211; https://tonybai.com/2023/02/22/rust-vs-go-in-2023</p>
<p>本文译自<a href="https://bitfieldconsulting.com/golang/rust-vs-go">《Rust vs Go in 2023》</a>。</p>
<blockquote>
<p>注：从2022年下半年开始，我们研发团队的产品研发不再局限于云端，车端也是将来的一个重要方向。于是我除了继续对Go语言保持常规的高度关注之外，也逐步开始留意Rust语言的发展。</p>
</blockquote>
<hr />
<p>Rust和Go哪个更好？Go还是Rust？在2023年，你应该为你的下一个项目选择哪种语言，为什么？两者在性能、简单性、安全性、功能、规模和并发性等方面如何比较？它们的共同点是什么，它们有哪些根本性的不同？让我们在这个友好而公平的Rust和Go的比较中找到答案。</p>
<h2>Rust和Go都很棒</h2>
<p>首先，我必须要说的是，<strong>Go和Rust都是绝对优秀的编程语言</strong>。它们都是现代的、强大的、被广泛采用的编程语言，并且都提供出色的性能。</p>
<p>你可能读过一些说Go比Rust好的文章，或者相反。但这真的没有意义；每一种编程语言都代表了一系列的权衡和取舍。每种语言都有自己的优化重点，所以你对语言的选择应该由适合你的东西和你想用它解决的问题决定。</p>
<p>在这篇文章中，我将尝试告诉你何时使用Go是理想选择以及何时使用Rust更佳。我也会试着介绍一下这两种语言的本质（如果你愿意的话，就是<a href="https://tonybai.com/2022/09/25/the-tao-of-go">Go和Rust的道</a>）。</p>
<p>虽然它们在语法和风格上有很大不同，但Rust和Go都是构建软件的一流工具。接下来，让我们仔细看看这两种语言。</p>
<h2>Go和Rust的相似之处</h2>
<p>Rust和Go有很多共同点，这也是你经常听到它们一起被提及的原因之一。两种语言的共同目标是什么呢？</p>
<blockquote>
<p>Rust是一种低级静态类型的多范式编程语言，专注于安全和性能。 &#8211; <a href="https://serokell.io/blog/rust-guide">Gints Dreimanis</a></p>
<p>Go是一种开源的编程语言，可以轻松构建简单、可靠、高效的软件。 &#8211; <a href="https://go.dev">go.dev</a></p>
</blockquote>
<h3>内存安全</h3>
<p>Go和Rust都属于现代编程语言，它们的首要任务是内存安全。经过几十年对C和C++等旧语言的使用，我们可以清楚地看到，导致错误和安全漏洞的最大原因之一是不安全地或不正确地访问内存。</p>
<p>Rust和Go以不同的方式处理这个问题，但它们的目标都是在管理内存方面比其他语言更聪明、更安全，并帮助你写出<a href="https://bitfieldconsulting.com/golang/crisp-code">正确</a>和<a href="https://bitfieldconsulting.com/golang/slower">高性能</a>的程序。</p>
<h3>快速、紧凑的可执行文件</h3>
<p>Go和Rust都是编译型语言，这意味着你的程序被直接翻译成可执行的机器码，因此你可以以单一二进制文件形式来部署你的程序；与<a href="https://bitfieldconsulting.com/golang/go-vs-python">Python</a>和Ruby等解释型语言不同，你不需要将解释器和大量的库和依赖关系与你的程序一起分发，这是一个很大的优点。这也使得Rust和Go的程序与解释型语言相比都非常快。</p>
<h3>通用语言</h3>
<p>Rust和Go都是强大的、可扩展的通用编程语言，你可以用它们来开发各种现代软件，从网络应用到分布式微服务，或者从嵌入式微控制器到移动应用程序。</p>
<p>两者都有优秀的标准库、繁荣的第三方生态系统以及巨大的商业支持和庞大的用户基础。它们都已经存在了很多年，并将在未来几年内继续被广泛使用。今天学习Go或Rust将是对你时间和精力的合理投资。</p>
<h3>务实的编程风格</h3>
<p>Go和Rust都不是<a href="https://bitfieldconsulting.com/golang/functional">以函数式编程为主的语言</a>（例如像Scala或Elixir），也不是完全面向对象的语言（像Java和C#）。相反，虽然Go和Rust都有与函数式和面向对象编程相关的特性，但它们是务实的语言，旨在以最合适的方式解决问题，而不是强迫你采用特定的做事方式。</p>
<p>如果你喜欢函数式编程风格，你会在Rust中发现更多对这种风格的支持，因为Rust在语法特性数量上要比Go更多。</p>
<blockquote>
<p>我们可以讨论什么是“面向对象”语言，但可以说C++、Java或C#用户所期望的面向对象编程风格在Go或Rust中都不存在。 &#8211; Jack Mott</p>
</blockquote>
<h3>规模化的开发</h3>
<p>Rust和Go都有一些有用的特性，使它们适合于大规模的编程，不管是指大型团队，还是大型代码库，或者两者兼具。</p>
<p>例如，C语言的程序员们多年来一直在争论将括号放在哪里，以及代码应该用制表符还是空格缩进，而Rust和Go通过使用标准的格式化工具（Go为gofmt，Rust为rustfmt）使用规范的风格自动重写你的代码，完全消除了这些问题。</p>
<p>这并不是说这种特殊的风格本身有多好：而是Rust和Go的程序员都喜欢这种<strong>标准化</strong>。</p>
<blockquote>
<p>gofmt的风格是没有人喜欢的，但gofmt却是所有人的最爱。 &#8211; <a href="https://www.youtube.com/watch?v=PAAkCSZUG1c&amp;t=8m43s">Rob Pike</a></p>
</blockquote>
<p>两种语言的另一个高分领域是<strong>构建管道(pipeline)</strong>。两种语言都有优秀的、内置的、高性能的标准构建和依赖管理工具；不再需要与复杂的第三方构建系统搏斗，也不再需要每隔几年就学习一个新的系统。</p>
<blockquote>
<p>对于早期职业生涯以Java和Ruby为背景的我而言，构建Go和Rust代码感觉就像从我的肩上卸下了一个不可能的重担。当我在谷歌工作时，遇到用Go编写的服务是一种解脱，因为我知道它很容易构建和运行。Rust也是如此，尽管我只在较小规模的Rust项目上工作过。我希望可无限配置的构建系统的时代已经过去了，所有语言都会有自己专门的构建工具，开箱即可使用。- <a href="https://samwho.dev/">山姆-罗斯</a></p>
</blockquote>
<h2>Rust还是Go？</h2>
<p>综上可知，这两种语言都设计得很好、很强大，那么你可能会想知道那些关于两门语言的“圣战”究竟是怎么回事（我也是）。为什么人们对“Go vs.Rust”如此大惊小怪，在社交媒体上大打出手，并且写长篇博文说只有傻瓜才会使用Rust，或者Go不是真正的编程语言，或者其他什么。</p>
<p>这可能会让他们感觉好些，但这并不能完全帮助你，因为你正试图决定在你的项目中使用哪种语言，或者你应该学习哪种语言来推动你的编程生涯。一个明智的人不会根据谁喊得声最大来做出重要的选择。</p>
<p>现在让我们继续我们成熟的讨论，看看在某些领域，一个有理智的人可能更喜欢哪一种语言。</p>
<h2>Go与Rust的性能对比</h2>
<p>我们已经说过，Go和Rust都能生产出高性能的程序，因为它们被编译成了本地机器代码，而不必通过解释器或虚拟机。</p>
<p>然而，Rust的性能尤其突出。它可以与C和C++相媲美，这两种语言通常被认为是性能最高的编译语言，但与这些老语言不同的是，Rust还提供了内存安全和并发安全，并且基本上不会给执行速度上带去没有任何开销。Rust还允许你创建复杂的抽象，而不需要在运行时付出任何性能上的代价。</p>
<p>相比之下，尽管Go程序的性能也非常好，但Go主要是为开发速度（包括编译）而设计的，而不是执行速度。Go程序员<a href="https://bitfieldconsulting.com/golang/slower">更倾向于清晰的代码而不是快速的代码</a>。</p>
<p>Go编译器也不会花很多时间去尝试生成最有效的机器代码；它更关心的是快速编译大量代码。所以Rust通常会在运行时基准测试中击败Go。</p>
<p>Rust的运行时性能也是一致和可预测的，因为它不使用垃圾收集。Go的垃圾收集器非常高效，并且经过优化，使其“STW(停止世界)”的停顿时间尽可能短（每一个新的Go版本都会越来越短）。但是垃圾收集不可避免地在程序的行为方式中引入了一些不可预测的因素，这在某些应用中可能是一个严重的问题，例如嵌入式系统。</p>
<p>因为Rust旨在让程序员完全控制底层硬件，所以有可能将Rust程序优化到相当接近机器的最大理论性能。这使得Rust在执行速度胜过所有其他考虑因素的领域是一个很好的选择，比如游戏编程、操作系统内核、网络浏览器组件和实时控制系统。</p>
<h2>简单性</h2>
<p>如果没有人能够弄清楚如何使用一种编程语言，那么这种语言有多快也无所谓。Go语言是为了应对C++等语言不断增长的复杂性而特意设计的；它的语法非常少，关键字也非常少，事实上，功能特性也很少。</p>
<p>这意味着<a href="http://gk.link/a/10AVZ">学习Go语言</a>不需要很长时间，就可以用它来编写有用的程序。</p>
<blockquote>
<p>Go是非常容易学习的。我知道这是一个经常被吹捧的好处，但我真的很惊讶于我能够如此迅速地提高工作效率。多亏了这个语言、文档和工具，我在两天后就写出了有趣的、可提交的代码。 &#8211; <a href="https://medium.com/better-programming/early-impressions-of-go-from-a-rust-programmer-f4fd1074c410">一个Rust程序员对Go的早期印象</a></p>
</blockquote>
<p>这里的关键词是<strong>简单性</strong>。当然，简单并不等同于容易，但是小而简单的语言比大而复杂的语言更容易学习。Go语言没有提供那么多不同的方法来做一件事情，所以所有写得好的Go代码往往看起来都一样。快速学习一个不熟悉的服务并理解它在做什么很容易。</p>
<pre><code>fmt.Println("Gopher's Diner Breakfast Menu")
for dish, price := range menu {
    fmt.Println(dish, price)
}
</code></pre>
<p>在我的<a href="https://bitfieldconsulting.com/code-club">代码俱乐部视频系列</a>中，我正是这样做的：从GitHub上半随机地挑选Go项目，并与一群Go初学者一起探索它们，看看我们能理解多少的代码。结果总是比我们预期的要多。</p>
<p>虽然核心语言很小，但Go的标准库却非常强大。这意味着你的学习曲线也需要包括你需要的标准库的部分，而不仅仅是Go语法。</p>
<p>另一方面，将功能从语言中转移到标准库中，意味着你可以只专注于学习与你现在相关的库。</p>
<p>Go也是为大规模的软件开发而设计的，支持有大型代码库的大型团队。在这种情况下，新的开发人员能够尽快上手是非常重要的。出于这个原因，Go社区十分看重：<a href="https://bitfieldconsulting.com/golang/commandments">简单、明显、常规、直接的程序</a>。</p>
<blockquote>
<p>使用Go，你可以快速完成工作。Go是我所使用过的生产力最高的语言之一。它的口号是：今天解决实际问题。 &#8211; <a href="https://endler.dev/2017/go-vs-rust/">马蒂亚斯-恩德勒</a></p>
</blockquote>
<h2>特性</h2>
<blockquote>
<p>Rust比其他几种编程语言支持更多的复杂语法特性，因此，你可以用它实现更多。 &#8211; <a href="https://devathon.com/blog/rust-vs-go-which-programming-language-to-choose/">devathon</a></p>
</blockquote>
<p>Rust是专门设计用来帮助程序员用最少的代码做最多的事情，它包括很多强大而有用的功能特性。例如，Rust的match功能可以让你以十分简洁地方式写出灵活的、富有表现力的逻辑：</p>
<pre><code>fn is_prime(n: u64) -&gt; bool {
    match n {
        0...1 =&gt; false,
        _ =&gt; !(2..n).any(|d| n % d == 0),
    }
}
</code></pre>
<p>因为Rust做了很多事情，这意味着有很多东西需要学习，特别是在开始的时候。但这没关系：在C++或Java中也有很多东西要学，而且你不会得到Rust的高级特性，比如内存安全。</p>
<p>批评Rust是一种复杂的语言忽略了一点：它被设计成具有表现力，这意味着有很多功能，而在许多情况下，这正是你想要的编程语言。</p>
<p>当然，Rust有一个学习曲线，但一旦你开始使用它，你就会好起来。</p>
<blockquote>
<p>对于那些准备接受更复杂的语法和语义（以及可能更高的可读性成本）以换取最大可能的性能的程序员来说，Rust将与C++和D语言争夺思想份额。 &#8211; <a href="https://dave.cheney.net/2015/07/02/why-go-and-rust-are-not-competitors">戴夫-切尼</a></p>
</blockquote>
<p>虽然Rust采用了Go的一些特性，而Go也在采用Rust的一些特性（尤其是<a href="https://bitfieldconsulting.com/golang/generics">泛型</a>），但可以说Rust的特性很重，而Go的特性相对较轻。</p>
<h2>并发</h2>
<p>大多数语言都对并发编程（同时做多件事情）有某种形式的支持，但Go从一开始就是为这项工作而设计的。Go不使用操作系统的线程，而是提供了一个轻量级的替代方案：<strong>goroutine</strong>。</p>
<p>每个goroutine是一个独立执行的Go函数，Go调度器会将其映射到其控制下的一个操作系统线程中。这意味着调度器可以非常有效地管理大量并发的goroutine，只使用有限的操作系统线程。</p>
<p>因此，你可以在一个程序中运行数百万个并发的goroutine，而不会产生严重的性能问题。这使得Go成为高规模并发应用程序的完美选择，如网络服务器和微服务。</p>
<p>Go还具有快速、安全、高效的功能特性，可以使用channel让goroutines进行通信和共享数据。Go的并发支持感觉设计得很好，使用起来也很愉快。</p>
<p>一般来说，对并发程序进行推断是很难的，而且在任何语言中建立可靠、正确的并发程序都是一个挑战。但由于它从一开始就内置于语言中，而不是事后才想到的，Go中的并发编程是最简单、最完整的。</p>
<blockquote>
<p>Go语言可以很容易地建立一个很好的多因素的应用程序，充分利用并发性，同时作为一组微服务进行部署。Rust也可以做这些事情，但可以说它更难。 在某些方面，Rust对防止与内存有关的安全漏洞的痴迷意味着程序员必须不遗余力地执行那些在其他语言（包括Go）中会更简单的任务。 &#8211; <a href="https://sdtimes.com/softwaredev/the-developers-dilemma-choosing-between-go-and-rust/">Sonya Koptyev</a></p>
</blockquote>
<p>相比之下，Rust中的并发故事是非常新的，而且还在稳定中，但它正处于非常积极的开发中，所以请关注这个领域。例如，Rust的<a href="https://github.com/rayon-rs/rayon">rayon库</a>提供了一种非常优雅和轻量级的方式来将顺序计算转化为并行计算。</p>
<blockquote>
<p>拥有goroutines和使用channel的轻量级语法真的很好。这真的显示了语法的力量，这些小细节使并发编程比其他语言感觉好得多 &#8211; <a href="https://medium.com/better-programming/early-impressions-of-go-from-a-rust-programmer-f4fd1074c410">一个Rust程序员对Go的早期印象</a></p>
</blockquote>
<p>虽然在Rust中实现并发程序可能不那么简单，但还是有可能的，而且这些程序可以利用Rust的安全保证。</p>
<p>一个很好的例子是标准库的Mutex类：在Go中，你可以忘记在访问某些东西之前获得一个Mutex锁，但Rust不会让你这样做。</p>
<blockquote>
<p>Go专注于将并发性作为一个一等公民的概念。这并不是说你不能在Rust中找到Go的面向actor的并发性，但这是留给程序员的一个练习。 &#8211; <a href="https://dave.cheney.net/2015/07/02/why-go-and-rust-are-not-competitors">Dave Cheney</a></p>
</blockquote>
<h2>安全</h2>
<p>我们在前面看到，Go和Rust都以不同的方式来防止一大类与内存管理有关的常见编程错误。但是Rust尤其努力确保你不会做一些你不想做的不安全的事情。</p>
<blockquote>
<p>Rust的编译器非常严格和学究派，它检查你使用的每个变量和你引用的每个内存地址。它避免了可能的数据竞争条件，并告知你未定义的行为。并发和内存安全问题在Rust的安全子集中根本不可能发生。 &#8211; <a href="https://bitbucket.org/blog/why-rust">为什么是Rust？</a></p>
</blockquote>
<p>这将使Rust编程成为与几乎所有其他语言不同的体验，而且一开始可能是一种挑战。但对很多人来说，这种辛苦是值得的。</p>
<blockquote>
<p>对我来说，Rust的关键优势是一种感觉，即编译器是我的后盾，不会让它可能检测到的任何错误通过（说真的，有时感觉就像魔法一样）。 &#8211; Grzegorz Nosek</p>
</blockquote>
<p>包括Go在内的许多语言都有帮助程序员避免错误的设施，但Rust将这一点提高到了一个新的水平，因此可能不正确的程序甚至不会被编译。</p>
<blockquote>
<p>有了Rust，库程序员有很多工具来防止他/她的用户犯错。Rust让我们有能力说，我们拥有一块特定的数据；其他东西不可能声称拥有，所以我们知道没有其他东西能够修改它。我想不出以前有什么时候我被赋予过这么多工具来防止意外的误用。这是一种奇妙的感觉。 &#8211; <a href="https://samwho.dev/">山姆-罗斯</a></p>
</blockquote>
<p>“与借用检查器(borrow checker)斗争”是Rust程序员新手的常见综合症，但在大多数情况下，它所发现的问题是你的代码中真正的bug（或至少是潜在的bug）。它可能会迫使你从根本上重构你的程序，以避免遇到这些问题；而当正确性和可靠性是你的首要任务时，这是件好事。</p>
<p>一个不改变你编程方式的语言有什么意义呢？当你用其他语言工作时，Rust所教授的关于安全的课程也是有用的。</p>
<blockquote>
<p>如果你选择了Rust，通常你需要该语言提供的保证：针对空指针和数据竞争的安全，可预测的运行时行为，以及对硬件的完全控制。如果你不需要这些功能，Rust可能是你下一个项目的糟糕选择。这是因为这些保证是有代价的：入门时间。你需要戒掉坏习惯，学习新概念。有可能的是，当你开始的时候，你会经常和借用检查器斗争。 &#8211; <a href="https://endler.dev/2017/go-vs-rust/">Matthias Endler</a></p>
</blockquote>
<p>你觉得Rust的编程模型有多大的挑战性，可能取决于你以前有哪些其他语言的经验。Python或Ruby程序员可能会发现它的限制性；其他人会很高兴。</p>
<blockquote>
<p>如果你是一个花了几周的时间来追寻内存安全漏洞的C/C++程序员，你会非常欣赏Rust。”与借用检查器斗争”变成了”编译器可以检测到这个？酷！” -Grzegorz Nosek</p>
</blockquote>
<h2>规模化</h2>
<blockquote>
<p>今天的服务器程序由数千万行代码组成，由数百甚至数千名程序员进行构建，而且每天都在更新。Go的设计和开发是为了使在这种环境中工作更有成效。Go的设计考虑包括严格的依赖性管理，随着系统的发展，软件架构的适应性，以及组件之间的健壮性。 &#8211; <a href="https://talks.golang.org/2012/splash.article">Rob Pike</a></p>
</blockquote>
<p>当你一个人或在小团队中处理问题时，选择简单的语言还是功能丰富的语言是一个偏好的问题。但是当软件越来越大，越来越复杂，团队越来越大时，差异就开始显现出来了。</p>
<p>对于大型应用程序和分布式系统来说，执行速度不如开发速度重要：像Go这样刻意简化的语言可以减少新开发人员的启动时间，并使他们更容易处理大型代码库的工作。</p>
<blockquote>
<p>有了Go，作为初级开发者更容易提高工作效率，而作为中级开发者则更难引入会导致后续问题的脆弱抽象。由于这些原因，Rust在企业软件开发方面不如Go有说服力。 &#8211; <a href="https://kristoff.it/blog/why-go-and-not-rust">Loris Cro</a></p>
</blockquote>
<p>当涉及到大型的软件开发时，清晰的比聪明的好。Go的局限性实际上使它比Rust等更复杂和强大的语言更适合企业和大机构。</p>
<h2>Rust和Go的不同点</h2>
<p>虽然Rust和Go都是流行的、现代的、广泛使用的语言，但它们并不是真正的竞争对手，因为它们故意针对的是完全不同的使用情况。</p>
<p><a href="https://tonybai.com/2022/09/25/the-tao-of-go">Go的整个编程方法</a>与Rust的完全不同，每一种语言都适合一些人，同时也会刺激另一些人。这完全没问题，如果Rust和Go都能以或多或少相同的方式做同样的事情，我们就不会真的需要两种不同的语言。</p>
<p>那么，我们是否可以通过发现Rust和Go所采取的截然不同的方法来了解它们各自的本性呢？让我们拭目以待。</p>
<h3>垃圾回收</h3>
<p>“要不要垃圾回收”是一个没有正确答案的问题。垃圾回收，以及一般的自动内存管理，使得开发可靠、高效的程序变得快速和容易，对于一些人来说，这至关重要。</p>
<p>但也有人说，垃圾回收及其性能开销和停顿，使程序在运行时表现得不可预测，并引入了不可接受的延迟。争论还在继续。</p>
<blockquote>
<p>Go是一种与Rust非常不同的语言。虽然两者都可以被模糊地描述为系统语言或C语言的替代品，但它们有不同的目标和应用、语言设计的风格以及优先级。垃圾回收是一个真正巨大的区别。Go中的GC使语言更简单，更小，更容易推理。在Rust中没有GC会让它变得非常快（尤其是当你需要保证延迟，而不仅仅是高吞吐量的时候），并且可以实现Go中不可能实现的功能和编程模式（或者至少是在不牺牲性能的情况下）。 &#8211; <a href="https://medium.com/better-programming/early-impressions-of-go-from-a-rust-programmer-f4fd1074c410">PingCAP</a></p>
</blockquote>
<h3>接近机器</h3>
<p>计算机编程的历史是一个越来越复杂的抽象的故事，它让程序员在解决问题时不用太担心底层机器的实际运作。</p>
<p>这使得程序更容易编写，也许更容易移植。但是对于许多程序来说，对硬件的访问以及对程序执行方式的精确控制更为重要。</p>
<p>Rust的目标是让程序员“更接近机器”，有更多的控制权，但Go抽象了架构细节，让程序员更接近问题。</p>
<blockquote>
<p>两种语言都有不同的适用范围。Go在编写微服务和典型的”DevOps”任务方面表现出色，但它不是一种系统编程语言。Rust对于那些看重并发性、安全性和性能的任务中更强；但它的学习曲线比Go更陡峭。 &#8211; <a href="https://endler.dev/2017/go-vs-rust/">Matthias Endler</a></p>
</blockquote>
<h3>必须运行更快</h3>
<p>许多人同意，对于大多数程序来说，<a href="https://bitfieldconsulting.com/golang/slower">性能不如可读性重要</a>。但当性能确实重要时，它真的很重要。Rust做了一些设计上的权衡，以达到尽可能好的执行速度。</p>
<p>相比之下，Go更关注简单性，它愿意为此牺牲一些（运行时）性能。但是Go的构建速度是无可匹敌的，这对于大型代码库来说是非常重要的。</p>
<blockquote>
<p>Rust比Go快。在基准测试中，Rust更快，在某些情况下，甚至是数量级的快。但在你选择用Rust写所有东西之前，考虑一下Go在许多基准测试中并不落后于它，而且它仍然比Java、C#、JavaScript、Python等快得多。如果你需要的是顶级的性能，那么选择这两种语言中的任何一种，你都会在游戏中领先。如果你正在构建一个处理高负载的网络服务，你希望能够在纵向和横向上进行扩展，那么这两种语言都会非常适合你。- <a href="https://codeburst.io/should-i-rust-or-should-i-go-59a298e00ea9">安德鲁-拉德</a></p>
</blockquote>
<h3>正确性</h3>
<p>另一方面，如果一个程序不需要正常工作的话，它可以任意地快。大多数代码不是为长期而写的，但有些程序能在生产中运行多长时间往往是令人惊讶的：在某些情况下，可以保持几十年。</p>
<p>在这种情况下，值得在开发中多花一点时间，以确保程序的正确性、可靠性，并在未来不需要大量的维护。</p>
<p>Go和Rust都旨在帮助你编写正确的程序，但方式不同。例如，Go提供了一个极好的内置测试框架，而Rust则专注于使用其借用检查器消除运行时的错误。</p>
<blockquote>
<p>我认为。Go适用于明天必须交付的代码，而Rust适用于必须在未来五年内保持运行不动的代码。 &#8211; Grzegorz Nosek</p>
</blockquote>
<p>虽然Go和Rust对于任何严肃的项目来说都是很好的选择，但是让自己尽可能地了解每种语言及其特点是一个好主意。</p>
<p>归根结底，别人怎么想并不重要：只有你能决定哪种语言适合你和你的团队。</p>
<blockquote>
<p>如果你想加快开发速度，也许是因为你有许多不同的服务需要编写，或者你有一个庞大的开发团队，那么Go是你的首选语言。Go把并发性作为第一等公民给你，并且不容忍不安全的内存访问（Rust也是如此），但不强迫你管理每一个细节。Go是快速和强大的，但它避免了使开发者陷入困境，而是专注于简单性和统一性。如果在另一方面，拧出每一盎司的性能是必要的，那么Rust应该是你的选择。 &#8211; <a href="https://codeburst.io/should-i-rust-or-should-i-go-59a298e00ea9">安德鲁-拉德</a></p>
</blockquote>
<h2>结论</h2>
<p>我希望这篇文章能让你相信Rust和Go都值得你认真考虑。如果可能的话，你应该争取在这两种语言中至少获得一定程度的经验，因为它们对你的任何技术职业都会有极大的帮助，甚至如果你仅把编程作为一种业余爱好的话。</p>
<p>如果你只有时间投资学习一门语言，在你将Go和Rust用于各种不同类型的大小程序之前，不要做出最终决定。</p>
<p>而编程语言的知识实际上只是成为一名成功的软件工程师的一小部分。到目前为止，你需要的最重要的技能是设计、工程、架构、沟通和协作。如果你在这些方面表现出色，无论你选择哪种语言，你都会成为一名优秀的软件工程师。学习愉快!</p>
<hr />
<p><a href="https://wx.zsxq.com/dweb2/index/group/51284458844544">“Gopher部落”知识星球</a>旨在打造一个精品Go学习和进阶社群！高品质首发Go技术文章，“三天”首发阅读权，每年两期Go语言发展现状分析，每天提前1小时阅读到新鲜的Gopher日报，网课、技术专栏、图书内容前瞻，六小时内必答保证等满足你关于Go语言生态的所有需求！2023年，Gopher部落将进一步聚焦于如何编写雅、地道、可读、可测试的Go代码，关注代码质量并深入理解Go核心技术，并继续加强与星友的互动。欢迎大家加入！</p>
<p><img src="http://image.tonybai.com/img/tonybai/gopher-tribe-zsxq-small-card.png" alt="img{512x368}" /><br />
<img src="http://image.tonybai.com/img/tonybai/go-programming-from-beginner-to-master-qr.png" alt="img{512x368}" /></p>
<p><img src="http://image.tonybai.com/img/tonybai/go-first-course-banner.png" alt="img{512x368}" /><br />
<img src="http://image.tonybai.com/img/tonybai/imooc-go-column-pgo-with-qr.jpg" alt="img{512x368}" /></p>
<p>著名云主机服务厂商DigitalOcean发布最新的主机计划，入门级Droplet配置升级为：1 core CPU、1G内存、25G高速SSD，价格5$/月。有使用DigitalOcean需求的朋友，可以打开这个<a href="https://m.do.co/c/bff6eed92687">链接地址</a>：https://m.do.co/c/bff6eed92687 开启你的DO主机之路。</p>
<p>Gopher Daily(Gopher每日新闻)归档仓库 &#8211; https://github.com/bigwhite/gopherdaily</p>
<p>我的联系方式：</p>
<ul>
<li>微博(暂不可用)：https://weibo.com/bigwhite20xx</li>
<li>微博2：https://weibo.com/u/6484441286</li>
<li>博客：tonybai.com</li>
<li>github: https://github.com/bigwhite</li>
</ul>
<p><img src="http://image.tonybai.com/img/tonybai/iamtonybai-wechat-qr.png" alt="" /></p>
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。</p>
<p style='text-align:left'>&copy; 2023, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2023/02/22/rust-vs-go-in-2023/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Go 1.20中值得关注的几个变化</title>
		<link>https://tonybai.com/2023/02/08/some-changes-in-go-1-20/</link>
		<comments>https://tonybai.com/2023/02/08/some-changes-in-go-1-20/#comments</comments>
		<pubDate>Wed, 08 Feb 2023 15:21:55 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[arena]]></category>
		<category><![CDATA[Array]]></category>
		<category><![CDATA[benchstat]]></category>
		<category><![CDATA[Build]]></category>
		<category><![CDATA[Cgo]]></category>
		<category><![CDATA[comparable]]></category>
		<category><![CDATA[Compiler]]></category>
		<category><![CDATA[Context]]></category>
		<category><![CDATA[cover]]></category>
		<category><![CDATA[crypto]]></category>
		<category><![CDATA[ecdh]]></category>
		<category><![CDATA[error]]></category>
		<category><![CDATA[generics]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[go1.17]]></category>
		<category><![CDATA[go1.18]]></category>
		<category><![CDATA[go1.19]]></category>
		<category><![CDATA[go1.20]]></category>
		<category><![CDATA[GOCOVERDIR]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[govet]]></category>
		<category><![CDATA[http]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[loccount]]></category>
		<category><![CDATA[Makefile]]></category>
		<category><![CDATA[Opensource]]></category>
		<category><![CDATA[Package]]></category>
		<category><![CDATA[PGO]]></category>
		<category><![CDATA[Pointer]]></category>
		<category><![CDATA[port]]></category>
		<category><![CDATA[riscv64]]></category>
		<category><![CDATA[runtime]]></category>
		<category><![CDATA[Rust]]></category>
		<category><![CDATA[Slice]]></category>
		<category><![CDATA[stdlib]]></category>
		<category><![CDATA[unit-test]]></category>
		<category><![CDATA[unsafe]]></category>
		<category><![CDATA[vet]]></category>
		<category><![CDATA[wasi]]></category>
		<category><![CDATA[wasm]]></category>
		<category><![CDATA[WebAssembly]]></category>
		<category><![CDATA[wrap]]></category>
		<category><![CDATA[切片]]></category>
		<category><![CDATA[包]]></category>
		<category><![CDATA[密码学]]></category>
		<category><![CDATA[开源]]></category>
		<category><![CDATA[指针]]></category>
		<category><![CDATA[数组]]></category>
		<category><![CDATA[构建]]></category>
		<category><![CDATA[标准库]]></category>
		<category><![CDATA[比较]]></category>
		<category><![CDATA[泛型]]></category>
		<category><![CDATA[移植性]]></category>
		<category><![CDATA[类型参数]]></category>
		<category><![CDATA[编译器]]></category>
		<category><![CDATA[覆盖率]]></category>
		<category><![CDATA[语法]]></category>
		<category><![CDATA[运行时]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=3794</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2023/02/08/some-changes-in-go-1-20 美国时间2023年2月1日，唯一尚未退休的Go语言之父Robert Griesemer代表Go核心开发团队在Go官博撰文正式发布了Go 1.20版本。就像Russ Cox在2022 GopherCon大会所说的那样：Go2永不会到来，Go 1.x.y将无限延续！ 注：似乎新兴编程语言都喜欢停留在1.x.y上无限延续，譬如已经演化到1.67版本的Rust^_^。 在《Go，13周年》之后，Go 1.20新特性在开发主干冻结(2022.11)之前，我曾写过一篇《Go 1.20新特性前瞻》，对照着Go 1.20 milestone中内容，把我认为的主要特性和大家简单过了一遍，不过那时Go 1.20毕竟没有正式发布，前瞻肯定不够全面，某些具体的点与正式版本可能也有差异！现在Go 1.20版本正式发布了，其Release Notes也补充完整了，在这一篇中，我再来系统说说Go 1.20版本中值得关注的那些变化。对于在前瞻一文中详细介绍过的特性，这里不会再重复讲解了，大家参考前瞻一文中的内容即可。而对于其他一些特性，或是前瞻一文中着墨不多的特性，这里会挑重点展开说说。 按照惯例，我们依旧首先来看看Go语法层面都有哪些变化，这可能也是多数Gopher们最为关注的变化点。 一. 语法变化 Go秉持“大道至简”的理念，对Go语法特性向来是“不与时俱进”的。自从Go 1.18大刀阔斧的加入了泛型特性后，Go语法特性就又恢复到了之前的“新三年旧三年，缝缝补补又三年”的节奏。Go 1.20亦是如此啊！Release Notes说Go 1.20版本在语言方面包含了四点变化，但看了变化的内容后，我觉得真正的变化只有一个，其他的都是修修补补。 1. 切片到数组的转换 唯一算是真语法变化的特性是支持切片类型到数组类型(或数组类型的指针)的类型转换，这个特性在前瞻一文中系统讲过，这里就不赘述了，放个例子大家直观认知一下就可以了： // https://github.com/bigwhite/experiments/blob/master/go1.20-examples/lang/slice2arr.go func slice2arrOK() { var sl = []int{1, 2, 3, 4, 5, 6, 7} var arr = [7]int(sl) var parr = (*[7]int)(sl) fmt.Println(sl) // [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/some-changes-in-go-1-20-1.png" alt="" /></p>
<p><a href="https://tonybai.com/2023/02/08/some-changes-in-go-1-20">本文永久链接</a> &#8211; https://tonybai.com/2023/02/08/some-changes-in-go-1-20</p>
<p>美国时间2023年2月1日，唯一尚未退休的Go语言之父<a href="https://github.com/griesemer">Robert Griesemer</a>代表Go核心开发团队在<a href="https://go.dev/blog/go1.20">Go官博撰文正式发布了Go 1.20版本</a>。就像<a href="https://www.youtube.com/watch?v=v24wrd3RwGo">Russ Cox在2022 GopherCon大会</a>所说的那样：<strong><a href="https://tonybai.com/2022/12/29/the-2022-review-of-go-programming-language">Go2永不会到来，Go 1.x.y将无限延续</a></strong>！</p>
<blockquote>
<p>注：似乎新兴编程语言都喜欢停留在1.x.y上无限延续，譬如已经<a href="https://www.rust-lang.org">演化到1.67版本的Rust</a>^_^。</p>
</blockquote>
<p>在<a href="https://tonybai.com/2022/11/11/go-opensource-13-years/">《Go，13周年》</a>之后，Go 1.20新特性在开发主干冻结(2022.11)之前，我曾写过一篇《<a href="https://tonybai.com/2022/11/17/go-1-20-foresight">Go 1.20新特性前瞻</a>》，对照着<a href="https://github.com/golang/go/milestone/250">Go 1.20 milestone</a>中内容，把我认为的主要特性和大家简单过了一遍，不过那时Go 1.20毕竟没有正式发布，前瞻肯定不够全面，某些具体的点与正式版本可能也有差异！现在Go 1.20版本正式发布了，其<a href="https://go.dev/blog/go1.20">Release Notes</a>也补充完整了，在这一篇中，我再来系统说说Go 1.20版本中值得关注的那些变化。对于在<a href="https://tonybai.com/2022/11/17/go-1-20-foresight">前瞻一文</a>中详细介绍过的特性，这里不会再重复讲解了，大家参考前瞻一文中的内容即可。而对于其他一些特性，或是前瞻一文中着墨不多的特性，这里会挑重点展开说说。</p>
<p>按照惯例，我们依旧首先来看看Go语法层面都有哪些变化，这可能也是多数Gopher们最为关注的变化点。</p>
<h2>一. 语法变化</h2>
<p>Go秉持“大道至简”的理念，对Go语法特性向来是“不与时俱进”的。自从<a href="https://tonybai.com/2022/04/20/some-changes-in-go-1-18">Go 1.18大刀阔斧的加入了泛型特性</a>后，Go语法特性就又恢复到了之前的“新三年旧三年，缝缝补补又三年”的节奏。Go 1.20亦是如此啊！Release Notes说Go 1.20版本在语言方面包含了四点变化，但看了变化的内容后，我觉得真正的变化只有一个，其他的都是修修补补。</p>
<h3>1. 切片到数组的转换</h3>
<p>唯一算是真语法变化的特性是支持<strong>切片类型到数组类型(或数组类型的指针)的类型转换</strong>，这个特性在<a href="https://tonybai.com/2022/11/17/go-1-20-foresight">前瞻一文</a>中系统讲过，这里就不赘述了，放个例子大家直观认知一下就可以了：</p>
<pre><code>// https://github.com/bigwhite/experiments/blob/master/go1.20-examples/lang/slice2arr.go

func slice2arrOK() {
    var sl = []int{1, 2, 3, 4, 5, 6, 7}
    var arr = [7]int(sl)
    var parr = (*[7]int)(sl)
    fmt.Println(sl)  // [1 2 3 4 5 6 7]
    fmt.Println(arr) // [1 2 3 4 5 6 7]
    sl[0] = 11
    fmt.Println(arr)  // [1 2 3 4 5 6 7]
    fmt.Println(parr) // &amp;[11 2 3 4 5 6 7]
}

func slice2arrPanic() {
    var sl = []int{1, 2, 3, 4, 5, 6, 7}
    fmt.Println(sl)
    var arr = [8]int(sl) // panic: runtime error: cannot convert slice with length 7 to array or pointer to array with leng  th 8
    fmt.Println(arr)     // &amp;[11 2 3 4 5 6 7]

}

func main() {
    slice2arrOK()
    slice2arrPanic()
}
</code></pre>
<p>有两点注意一下就好：</p>
<ul>
<li>切片转换为数组类型的指针，那么该指针将指向切片的底层数组，就如同上面例子中slice2arrOK的parr变量那样；</li>
<li>转换的数组类型的长度不能大于原切片的长度(注意是长度而不是切片的容量哦)，否则在运行时会抛出panic。</li>
</ul>
<h3>2. 其他的修修补补</h3>
<ul>
<li>comparable“放宽”了对泛型实参的限制</li>
</ul>
<p>下面代码在Go 1.20版本之前，比如Go 1.19版本中会无法通过编译：</p>
<pre><code>// https://github.com/bigwhite/experiments/blob/master/go1.20-examples/lang/comparable.go

func doSth[T comparable](t T) {
}

func main() {
    n := 2
    var i interface{} = n // 编译错误：interface{} does not implement comparable
    doSth(i)
}
</code></pre>
<p>之前，comparable约束下的泛型形参需要支持严格可比较(strictly comparable)的类型作为泛型实参，哪些是严格可比较的类型呢？Go 1.20的语法规范做出了进一步澄清：如果一个类型是可比较的，且不是接口类型或由接口类型组成的类型，那么这个类型就是<strong>严格可比较的类型</strong>，包括：</p>
<pre><code>- 布尔型、数值类型、字符串类型、指针类型和channel是严格可比较的。
- 如果结构体类型的所有字段的类型都是严格可比较的，那么该结构体类型就是严格可比较的。
- 如果数组元素的类型是严格可比较的，那么该数组类型就是严格可比较的。
- 如果类型形参的类型集合中的所有类型都是严格可比较的，那么该类型形参就是严格可比较的。
</code></pre>
<p>我们看到：例外的就是接口类型了。接口类型不是“严格可比较的(strictly comparable)”，但未作为类型形参的接口类型是可比较的(comparable)，如果两个接口类型的动态类型相同且值相等，那么这两个接口类型就相等，或两个接口类型的值均为nil，它们也相等，否则不等。</p>
<p>Go 1.19版本及之前，作为非严格比较类型的接口类型是不能作为comparable约束的类型形参的类型实参的，就像上面comparable.go中示例代码那样，但Go 1.20版本开始，这一要求被防控，接口类型被允许作为类型实参赋值给comparable约束的类型形参了！不过这么做之前，你也要明确一点，如果像下面这样两个接口类型底层类型相同且是不可比较的类型（比如切片)，那么代码将在运行时抛panic：</p>
<pre><code>// https://github.com/bigwhite/experiments/blob/master/go1.20-examples/lang/comparable1.go

func doSth[T comparable](t1, t2 T) {
    if t1 != t2 {
        println("unequal")
        return
    }
    println("equal")
}

func main() {
    n1 := []byte{2}
    n2 := []byte{3}
    var i interface{} = n1
    var j interface{} = n2
    doSth(i, j) // panic: runtime error: comparing uncomparable type []uint8
}
</code></pre>
<p>Go 1.20语言规范借此机会还进一步澄清了结构体和数组两种类型比较实现的规范：对于结构体类型，Go会按照结构体字段的声明顺序，逐一字段进行比较，直到遇到第一个不相等的字段为止。如果没有不相等字段，则两个结构体字段相等；对于数组类型，Go会按数组元素的顺序，逐一元素进行比较，直到遇到第一个不相等的元素为止。如果没有不相等的元素，则两个数组相等。</p>
<ul>
<li>unsafe包继续添加“语法糖”</li>
</ul>
<p>继<a href="https://tonybai.com/2021/08/17/some-changes-in-go-1-17">Go 1.17版本</a>在unsafe包增加Slice函数后，Go 1.20版本又增加三个语法糖函数：SliceData、String和StringData：</p>
<pre><code>// $GOROOT/src/unsafe/unsafe.go
func SliceData(slice []ArbitraryType) *ArbitraryType
func String(ptr *byte, len IntegerType) string
func StringData(str string) *byte
</code></pre>
<p>值得注意的是由于string的不可更改性，String函数的参数ptr指向的内容以及StringData返回的指针指向的内容在String调用和StringData调用后不允许修改，但实际情况是怎么样的呢？</p>
<pre><code>// https://github.com/bigwhite/experiments/blob/master/go1.20-examples/lang/unsafe.go

func main() {
    var arr = [6]byte{'h', 'e', 'l', 'l', 'o', '!'}
    s := unsafe.String(&amp;arr[0], 6)
    fmt.Println(s) // hello!
    arr[0] = 'j'
    fmt.Println(s) // jello!

    b := unsafe.StringData(s)
    *b = 'k'
    fmt.Println(s) // kello!

    s1 := "golang"
    fmt.Println(s1) // golang
    b = unsafe.StringData(s1)
    *b = 'h' // fatal error: fault, unexpected fault address 0x10a67e5
    fmt.Println(s1)
}
</code></pre>
<p>我们看到：unsafe.String函数调用后，如果我们修改了传入的指针指向的内容，那么该改动会影响到后续返回的string内容！而StringData返回<br />
的指针所指向的内容一旦被修改，其结果要根据字符串的来源而定了。对于由可修改的底层数组“创建”的字符串(如s)，通过StringData返回的指<br />
针可以“修改”字符串的内容；而对于由字符串字面值初始化的字符串变量(如s1)，其内容是不可修改的(编译器将字符串底层存储分配在了只读数据区)，尝试通过指针修改指向内容，会导致运行时的段错误。</p>
<h2>二. 工具链</h2>
<h3>1. Go安装包“瘦身”</h3>
<p>这些年，Go发布版的安装包“体格”是越来越壮了，动辄100多MB的压缩包，以go.dev/dl页面上的go1.xy.linux-amd64.tar.gz为例，我们看看从Go 1.15版本到Go 1.19版本的“体格”变化趋势：</p>
<pre><code>Go 1.15 - 116MB
Go 1.16 - 123MB
Go 1.17 - 129MB
Go 1.18 - 135MB
Go 1.19 - 142MB
</code></pre>
<p>如果按此趋势，Go 1.20势必要上到150MB以上。但Go团队找到了“瘦身”方法，那就是：<a href="https://github.com/golang/go/issues/47257">从Go 1.20开始发行版的安装包不再为GOROOT中的软件包提供预编译的.a文件了</a>，这样我们得到的瘦身后的Go 1.20版本的size为<strong>95MB</strong>！相较于Go 1.19，Go 1.20的安装包“瘦”了三分之一。安装包解压后这种体现更为明显：</p>
<pre><code>➜  /Users/tonybai/.bin/go1.19 git:(master) ✗ $du -sh
495M    .
➜  /Users/tonybai/.bin/go1.20 git:(master) ✗ $du -sh
265M    .
</code></pre>
<p>我们看到：Go 1.20占用的磁盘空间仅为Go 1.19版本的一半多一点而已。 并且，Go 1.20版本中，GOROOT下的源码将像其他用户包那样在构建后被缓存到本机cache中。此外，go install也不会为GOROOT下的软件包安装.a文件。</p>
<h3>2. 编译器</h3>
<h4>1) PGO(profile-guided optimization)</h4>
<p>Go 1.20编译器的一个最大的变更点是引入了PGO优化技术预览版，这个在前瞻一文中也有<a href="https://tonybai.com/2022/11/17/go-1-20-foresight">对PGO技术的简单介绍</a>。说白了点，PGO技术就是在原有compiler优化技术的基础上，针对程序在生产环境运行中的热点关键路径再进行一轮优化，并且针对热点代码执行路径，编译器会放开一些限制，比如<a href="https://tonybai.com/2022/10/17/understand-go-inlining-optimisations-by-example">Go决定是否对函数进行内联优化的复杂度上限默认值是80</a>，但对于PGO指示的关键热点路径，即便函数复杂性超过80很多，也可能会被inline优化掉。</p>
<p>之前持续性能剖析工具开发商Polar Signals曾发布一篇文章<a href="https://www.polarsignals.com/blog/posts/2022/09/exploring-go-profile-guided-optimizations/">《Exploring Go&#8217;s Profile-Guided Optimizations》</a>，专门探讨了PGO技术可能带来的优化效果，文章中借助了Go项目中自带的测试示例，这里也基于这个示例带大家重现一下。</p>
<p>我们使用的例子在Go 1.20源码/安装包的\$GOROOT/src/cmd/compile/internal/test/testdata/pgo/inline路径下：</p>
<pre><code>$ls -l
total 3156
-rw-r--r-- 1 tonybai tonybai    1698 Jan 31 05:46 inline_hot.go
-rw-r--r-- 1 tonybai tonybai     843 Jan 31 05:46 inline_hot_test.go
</code></pre>
<p>我们首先执行一下inline目录下的测试，并生成用于测试的可执行文件以及对应的cpu profile文件供后续PGO优化使用：</p>
<pre><code>$go test -o inline_hot.test -bench=. -cpuprofile inline_hot.pprof
goos: linux
goarch: amd64
pkg: cmd/compile/internal/test/testdata/pgo/inline
cpu: Intel(R) Core(TM) i7-9700 CPU @ 3.00GHz
BenchmarkA-8        1348        870005 ns/op
PASS
ok      cmd/compile/internal/test/testdata/pgo/inline   1.413s
</code></pre>
<p>接下来，我们对比一下不使用PGO和使用PGO优化，Go编译器在内联优化上的区别：</p>
<pre><code>$diff &lt;(go test -run=none -tags='' -timeout=9m0s -gcflags="-m -m" 2&gt;&amp;1 | grep "can inline") &lt;(go test -run=none -tags='' -timeout=9m0s -gcflags="-m -m -pgoprofile inline_hot.pprof" 2&gt;&amp;1 | grep "can inline")
4a5,6
&gt; ./inline_hot.go:53:6: can inline (*BS).NS with cost 106 as: method(*BS) func(uint) (uint, bool) { x := int(i &gt;&gt; lWSize); if x &gt;= len(b.s) { return 0, false }; w := b.s[x]; w = w &gt;&gt; (i &amp; (wSize - 1)); if w != 0 { return i + T(w), true }; x = x + 1; for loop; return 0, false }
&gt; ./inline_hot.go:74:6: can inline A with cost 312 as: func() { s := N(100000); for loop; for loop }
</code></pre>
<blockquote>
<p>上面diff命令中为Go test命令传入-run=none -tags=”" -gcflags=”-m -m”是为了仅编译源文件，而不执行任何测试。</p>
</blockquote>
<p>我们看到，相较于未使用PGO优化的结果，PGO优化后的结果多了两个inline函数，这两个可以被inline的函数，一个的复杂度开销为106，一个是312，都超出了默认的80，但仍然可以被inline。</p>
<p>我们来看看PGO的实际优化效果，我们分为在无PGO优化与有PGO优化下执行100次benchmark，再用benchstat工具对比两次的结果：</p>
<pre><code>$go test -o inline_hot.test -bench=. -cpuprofile inline_hot.pprof -count=100 &gt; without_pgo.txt
$go test -o inline_hot.test -bench=. -gcflags="-pgoprofile inline_hot.pprof" -count=100 &gt; with_pgo.txt

$benchstat without_pgo.txt with_pgo.txt
goos: linux
goarch: amd64
pkg: cmd/compile/internal/test/testdata/pgo/inline
cpu: Intel(R) Core(TM) i7-9700 CPU @ 3.00GHz
    │ without_pgo.txt │            with_pgo.txt             │
    │     sec/op      │   sec/op     vs base                │
A-8       874.7µ ± 0%   872.6µ ± 0%  -0.24% (p=0.024 n=100)
</code></pre>
<blockquote>
<p>注：benchstat的安装方法：\$go install golang.org/x/perf/cmd/benchstat@latest</p>
</blockquote>
<p>我们看到，在我的机器上(ubuntu 20.04 linux kerenel 5.4.0-132)，PGO针对这个测试的优化效果并不明显(仅仅有0.24%的提升)，Polar Signals原文中的提升幅度也不大，仅为1.05%。</p>
<p>Go官方Release Notes中提到benchmark提升效果为3%~4%，同时官方也提到了，这个仅仅是PGO初始技术预览版，后续会加强对PGO优化的投入，直至对多数程序产生较为明显的优化效果。个人觉得目前PGO尚处于早期，不建议在生产中使用。</p>
<p>Go官方也增加针对<a href="https://go.dev/doc/pgo">PGO的ref页面</a>，大家重点看看其中的FAQ，你会有更多收获！</p>
<h4>2) 构建速度</h4>
<p>Go 1.18泛型落地后，Go编译器的编译速度出现了回退(幅度15%)，Go 1.19编译速度也没有提升。虽然编译速度回退后依然可以“秒杀”竞争对手，但对于以编译速度快著称的Go来说，这个问题必须修复。Go 1.20做到了这一点，让Go编译器的编译速度重新回归到了Go 1.17的水准！相对Go 1.19提升10%左右。</p>
<p>我使用github.com/reviewdog/reviewdog这个库实测了一下，分别使用go 1.17.1、go 1.18.6、go 1.19.1和Go 1.20对这个module进行go build -a构建(之前将依赖包都下载本地，排除掉go get环节的影响)，结果如下：</p>
<pre><code>go 1.20：
$time go build -a github.com/reviewdog/reviewdog/cmd/reviewdog
go build -a github.com/reviewdog/reviewdog/cmd/reviewdog  48.01s user 7.96s system 536% cpu 10.433 total

go 1.19.1：
$time go build -a github.com/reviewdog/reviewdog/cmd/reviewdog
go build -a github.com/reviewdog/reviewdog/cmd/reviewdog  54.40s user 10.20s system 506% cpu 12.757 total

go 1.18.6：
$time go build -a github.com/reviewdog/reviewdog/cmd/reviewdog
go build -a github.com/reviewdog/reviewdog/cmd/reviewdog  53.78s user 9.85s system 545% cpu 11.654 total

go 1.17.1：
$time go build -a github.com/reviewdog/reviewdog/cmd/reviewdog
go build -a github.com/reviewdog/reviewdog/cmd/reviewdog  50.30s user 9.76s system 580% cpu 10.338 total
</code></pre>
<p>虽然不能十分精确，但总体上反映出各个版本的编译速度水准以及Go 1.20相对于Go 1.18和Go 1.19版本的提升。我们看到Go 1.20与Go 1.17版本在一个水平线上，甚至要超过Go 1.17(但可能仅限于我这个个例)。</p>
<h4>3) 允许在泛型函数/方法中进行类型声明</h4>
<p>Go 1.20版本之前下面代码是无法通过Go编译器的编译的：</p>
<pre><code>// https://github.com/bigwhite/experiments/blob/master/go1.20-examples/tools/compiler/local_type_decl.go
package main

func F[T1 any]() {
    type x struct{} // 编译错误：type declarations inside generic functions are not currently supported
    type y = x      // 编译错误：type declarations inside generic functions are not currently supported
}

func main() {
    F[int]()
}
</code></pre>
<p><a href="https://github.com/golang/go/issues/47631">Go 1.20改进了语言前端的实现</a>，通过unified IR实现了对在泛型函数/方法中进行类型声明(包括定义type alias)的支持。</p>
<p>同时，Go 1.20在<a href="https://go.dev/ref/spec#Type_parameter_declarations">spec</a>中还明确了<a href="https://github.com/golang/go/issues/40882">哪些使用了递归方式声明的类型形参列表是不合法的</a>：</p>
<pre><code>type T1[P T1[P]] …                    // 不合法: 形参列表中作为约束的T1引用了自己
type T2[P interface{ T2[int] }] …     // 不合法: 形参列表中作为约束的T2引用了自己
type T3[P interface{ m(T3[int])}] …   // 不合法: 形参列表中作为约束的T3引用了自己

type T4[P T5[P]] …                    // 不合法: 形参列表中，T4引用了T5 并且
type T5[P T4[P]] …                    //          T5引用了T4

type T6[P int] struct{ f *T6[P] }     // 正确: 虽然引用了T6，但这个引用发生在结构体定义中而不是形参列表中
</code></pre>
<h4>4) 构建自举源码的Go编译器的版本选择</h4>
<p>Go从Go 1.5版本开始实现自举，即使用Go实现Go，那么自举后的Go项目是谁来编译的呢？最初对应编译Go 1.5版本的Go编译器版本为Go 1.4。</p>
<p>以前从源码构建Go发行版，当未设置GOROOT_BOOTSTRAP时，编译脚本会默认使用Go 1.4，但如果有更高版本的Go编译器存在，会使用更高版本的编译器。</p>
<p>Go 1.18和Go 1.19会首先寻找是否有go 1.17版本，如果没有再使用go 1.4。</p>
<p>Go 1.20会寻找当前Go 1.17的最后一个版本Go 1.17.13，如果没有，则使用Go 1.4。</p>
<p>将来，Go核心团队计划一年升级一次构建自举源码的Go编译器的版本，例如：Go 1.22版本将使用Go 1.20版本的编译器。</p>
<h4>5) cgo</h4>
<p>Go命令现在在没有C工具链的系统上会默认禁用了cgo。更具体来说，当CGO_ENABLED环境变量未设置，CC环境变量未设置以及PATH环境变量中没有找到默认的C编译器（通常是clang或gcc）时，CGO_ENABLED会被默认设置为0。</p>
<h3>3. 其他工具</h3>
<h4>1) 支持采集应用执行的代码盖率</h4>
<p>在前瞻一文中，我提到过Go 1.20将对代码覆盖率的支持扩展到了应用整体层面，而不再仅仅是unit test。这里使用一个例子来看一下，究竟如何采集应用代码的执行覆盖率。我们以gitlab.com/esr/loccount这个代码统计工具为例，先修改一下Makefile，在go build后面加上-cover选项，然后编译loccount，并对其自身进行代码统计：</p>
<pre><code>// /home/tonybai/go/src/gitlab.com/loccount
$make
$mkdir mycovdata
$GOCOVERDIR=./mycovdata loccount .
all          SLOC=4279    (100.00%) LLOC=1213    in 110 files
Go           SLOC=1724    (40.29%)  LLOC=835     in 3 files
asciidoc     SLOC=752     (17.57%)  LLOC=0       in 5 files
C            SLOC=278     (6.50%)   LLOC=8       in 2 files
Python       SLOC=156     (3.65%)   LLOC=0       in 2 files
... ...
</code></pre>
<p>上面执行loccount之前，我们建立了一个mycovdata目录，并设置GOCOVERDIR的值为mycovdata目录的路径。在这样的上下文下，执行loccount后，mycovdata目录下会生成一些覆盖率统计数据文件：</p>
<pre><code>$ls mycovdata
covcounters.4ec45ce64f965e77563ecf011e110d4f.926594.1675678144659536943  covmeta.4ec45ce64f965e77563ecf011e110d4f
</code></pre>
<p>怎么查看loccount的执行覆盖率呢？我们使用go tool covdata来查看：</p>
<pre><code>$go tool covdata percent -i=mycovdata
    loccount    coverage: 69.6% of statements
</code></pre>
<p>当然, covdata子命令还支持其他一些功能，大家可以自行查看manual挖掘。</p>
<h4>2) vet</h4>
<p>Go 1.20版本中，go工具链的vet子命令增加了两个十分实用的检测：</p>
<ul>
<li>对loopclosure这一检测策略进行了增强</li>
</ul>
<p>具体可参见https://github.com/golang/tools/tree/master/go/analysis/passes/loopclosure代码</p>
<ul>
<li>增加对2006-02-01的时间格式的检查</li>
</ul>
<p>注意我们使用time.Format或Parse时，最常使用的是2006-01-02这样的格式，即ISO 8601标准的时间格式，但一些代码中总是出现2006-02-01，十分容易导致错误。这个版本中，go vet将会对此种情况进行检查。</p>
<h2>三. 运行时与标准库</h2>
<h3>1. 运行时(runtime)</h3>
<p>Go 1.20运行时的调整并不大，仅对GC的内部数据结构进行了微调，这个调整可以获得最多2%的内存开销下降以及cpu性能提升。</p>
<h3>2. 标准库</h3>
<p>标准库肯定是变化最多的那部分。前瞻一文中对下面变化也做了详细介绍，这里不赘述了，大家可以翻看那篇文章细读：</p>
<ul>
<li>支持wrap multiple errors</li>
<li>time包新增DateTime、DateOnly和TimeOnly三个layout格式常量</li>
<li>新增arena包<br />
&#8230; &#8230;</li>
</ul>
<p>标准库变化很多，这里不能一一罗列，再补充一些我认为重要的，其他的变化大家可以到<a href="https://go.dev/doc/go1.20">Go 1.20 Release Notes</a>去看：</p>
<h4>1) arena包</h4>
<p>前瞻一文已经对arena包做了简要描述，对于arena包的使用以及最佳适用场合的探索还在进行中。著名持续性能剖析工具<a href="https://pyroscope.io/">pyroscope</a>的官方博客文章<a href="https://pyroscope.io/blog/go-1-20-memory-arenas/">《Go 1.20 arenas实践：arena vs. 传统内存管理》</a>对于arena实验特性的使用给出了几点好的建议，比如：</p>
<ul>
<li>只在关键的代码路径中使用arena，不要到处使用它们</li>
<li>在使用arena之前和之后对你的代码进行profiling，以确保你在能提供最大好处的地方添加arena。</li>
<li>密切关注arena上创建的对象的生命周期。确保你不会把它们泄露给你程序中的其他组件，因为那里的对象可能会超过arena的寿命。</li>
<li>使用defer a.Free()来确保你不会忘记释放内存。</li>
<li>如果你想在arena被释放后使用对象，使用arena.Clone()将其克隆回heap中。</li>
</ul>
<p>pyroscope的开发人员认为arena是一个强大的工具，也支持标准库中保留arena这个特性，但也建议将arena和reflect、unsafe、cgo等一样纳入“不推荐”使用的包行列。这点我也是赞同的。我也在考虑如何基于arena改进我们产品的协议解析器的性能，有成果后，我也会将实践过程分享出来的。</p>
<h4>2) 新增crypto/ecdh包</h4>
<p>密码学包(crypto)的主要maintainer <a href="https://filippo.io/">Filippo Valsorda</a>从google离职后，<a href="https://words.filippo.io/full-time-maintainer/">成为了一名专职开源项目维护者</a>。这似乎让其更有精力和动力对crypto包进行更好的规划、设计和实现了。<a href="https://github.com/golang/go/issues/52221">crypto/ecdh包就是在他的提议下加入到Go标准库中的</a>。</p>
<p>相对于标准库之前存在的crypto/elliptic等包，crypto/ecdh包的API更为高级，Go官方推荐使用ecdh的高级API，这样大家以后可以不必再与低级的密码学函数斗争了。</p>
<h4>3) HTTP ResponseController</h4>
<p>以前HTTP handler的超时都是http服务器全局指定一个的：包括ReadTimeout和WriteTimeout。但有些时候，如果能在某个请求范围内支持这些超时（以及可能的其他选项）将非常有用。Damien Neil就创建了这个<a href="https://github.com/golang/go/issues/54136">增加ResponseController的提案</a>，下面是一个在HandlerFunc中使用ResponseController的例子：</p>
<pre><code>http.HandleFunc("/foo", func(w http.ResponseWriter, r *http.Request) {
  ctl := http.NewResponseController(w, r)
  ctl.SetWriteDeadline(time.Now().Add(1 * time.Minute)) // 仅为这个请求设置deadline
  fmt.Fprintln(w, "Hello, world.") // 这个写入的timeout为1-minute
})
</code></pre>
<h4>4) context包增加WithCancelCause函数</h4>
<p>context包新增了一个WithCancelCause函数，与WithCancel不同，通过WithCancelCause返回的Context，我们可以得到cancel的原因，比如下面示例：</p>
<pre><code>// https://github.com/bigwhite/experiments/blob/master/go1.20-examples/library/context.go

func main() {
    myError := fmt.Errorf("%s", "myError")
    ctx, cancel := context.WithCancelCause(context.Background())
    cancel(myError)
    fmt.Println(ctx.Err())          // context.Canceled
    fmt.Println(context.Cause(ctx)) // myError
}
</code></pre>
<p>我们看到通过context.Cause可以得到Context在cancel时传入的错误原因。</p>
<h2>四. 移植性</h2>
<p>Go对新cpu体系结构和OS的支持向来是走在前面的。Go 1.20还新增了对freebsd在risc-v上的实验性支持，其环境变量为GOOS=freebsd, GOARCH=riscv64。但Go 1.20也将成为对下面平台提供支持的最后一个Go版本：</p>
<ul>
<li>Windows 7, 8, Server 2008和Server 2012</li>
<li>MacOS 10.13 High Sierra和10.14 (我的安装了10.14的mac os又要在go 1.21不被支持了^_^) </li>
</ul>
<p>近期Go团队又有了新提案：<a href="https://github.com/golang/go/issues/58141">支持WASI(GOOS=wasi GOARCH=wasm)</a>，WASI是啥，它是WebAssembly一套与引擎无关(engine-indepent)的、面向非Web系统的WASM API标准，是WebAssembly脱离浏览器的必经之路！一旦生成满足WASI的WASM程序，该程序就可以在任何支持WASI或兼容的runtime上运行。不出意外，该提案将在Go 1.21或Go 1.22版本落地。</p>
<p>本文中的示例代码可以在<a href="https://github.com/bigwhite/experiments/blob/master/go1.20-examples">这里</a>下载。</p>
<hr />
<p><a href="https://wx.zsxq.com/dweb2/index/group/51284458844544">“Gopher部落”知识星球</a>旨在打造一个精品Go学习和进阶社群！高品质首发Go技术文章，“三天”首发阅读权，每年两期Go语言发展现状分析，每天提前1小时阅读到新鲜的Gopher日报，网课、技术专栏、图书内容前瞻，六小时内必答保证等满足你关于Go语言生态的所有需求！2023年，Gopher部落将进一步聚焦于如何编写雅、地道、可读、可测试的Go代码，关注代码质量并深入理解Go核心技术，并继续加强与星友的互动。欢迎大家加入！</p>
<p><img src="http://image.tonybai.com/img/tonybai/gopher-tribe-zsxq-small-card.png" alt="img{512x368}" /><br />
<img src="http://image.tonybai.com/img/tonybai/go-programming-from-beginner-to-master-qr.png" alt="img{512x368}" /></p>
<p><img src="http://image.tonybai.com/img/tonybai/go-first-course-banner.png" alt="img{512x368}" /><br />
<img src="http://image.tonybai.com/img/tonybai/imooc-go-column-pgo-with-qr.jpg" alt="img{512x368}" /></p>
<p>著名云主机服务厂商DigitalOcean发布最新的主机计划，入门级Droplet配置升级为：1 core CPU、1G内存、25G高速SSD，价格5$/月。有使用DigitalOcean需求的朋友，可以打开这个<a href="https://m.do.co/c/bff6eed92687">链接地址</a>：https://m.do.co/c/bff6eed92687 开启你的DO主机之路。</p>
<p>Gopher Daily(Gopher每日新闻)归档仓库 &#8211; https://github.com/bigwhite/gopherdaily</p>
<p>我的联系方式：</p>
<ul>
<li>微博(暂不可用)：https://weibo.com/bigwhite20xx</li>
<li>微博2：https://weibo.com/u/6484441286</li>
<li>博客：tonybai.com</li>
<li>github: https://github.com/bigwhite</li>
</ul>
<p><img src="http://image.tonybai.com/img/tonybai/iamtonybai-wechat-qr.png" alt="" /></p>
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。</p>
<p style='text-align:left'>&copy; 2023, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2023/02/08/some-changes-in-go-1-20/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Go为什么能成功</title>
		<link>https://tonybai.com/2022/12/07/why-go-succeed/</link>
		<comments>https://tonybai.com/2022/12/07/why-go-succeed/#comments</comments>
		<pubDate>Wed, 07 Dec 2022 14:01:55 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[go-get]]></category>
		<category><![CDATA[go-module]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[Google]]></category>
		<category><![CDATA[parameter]]></category>
		<category><![CDATA[RobPike]]></category>
		<category><![CDATA[runtime]]></category>
		<category><![CDATA[多核]]></category>
		<category><![CDATA[显式]]></category>
		<category><![CDATA[泛型]]></category>
		<category><![CDATA[简单]]></category>
		<category><![CDATA[组合]]></category>
		<category><![CDATA[语法]]></category>
		<category><![CDATA[面向工程]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=3751</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2022/12/07/why-go-succeed 大家在入门Go语言时，多埋头于Go语法，忙于练手或快速完成公司的项目，无暇思考。 但当大家到了要进阶，要冲刺高级阶段的时候，我建议你不能再稀里糊涂了。既然入了Go这个坑，在进入高级阶段前，我们最好在门口的“影壁墙”前驻留一下。 仔细思考一下我们投入这么多精力研究的Go为什么能成功，后续还能否持续成功下去。你要有自己的基本的判断，自我暗示也好，坚定信心也罢，我们要为继续攀登Go高峰进行蓄能。 一. 头脑风暴一下Go成功的因素 相信无论针对哪个gopher群体做头脑风暴，让大家列举Go成功的因素，大家的主流答案也无外乎下图中这些： 图中的各个因素与Go的成功都不无干系，但是究竟哪个或哪几个是决定性的呢？ 二. Go成功的根本因素 很显然，这个问题是没有标准答案，是见仁见智的。这里我列举一下我的观点，供大家参考。 直接上结论，我认为Go成功的根本因素就一个：Google。 为什么这么说呢？下面我们展开来看(见下图)！ 我将Go社区比做一支军队，而Go就是Go社区的武器，与其他编程语言搏杀，占地盘(fans)。下面我们就来解构一下这支军队的构成以及为什么这支军队目前有诸多成功案例！ 1. Google为Go社区提供了统帅与武器 众所周知，2007年Google的三名员工Robert Griesemer、Rob Pike和Ken Thompson(retire很早，精神上领袖，给予Go名誉上的背书)一起发明了Go语言，2009年Go开源后，Go社区逐渐形成。统帅是一支军队的灵魂，他们做出了影响Go和Go社区的最初的也是最重要的决策和这背后的Go设计哲学！ a) 设计决策 在2022年，Go团队在美国计算机学会通讯(Communications of the ACM)期刊上发表paper：《Go编程语言与环境》，对当年做出的诸多决策做了细致说明，这里对其中两个最重要的决策做简单说明： Go旨在成为一个编程环境 Go语言之父们认为语言特性仅是编程语言的一部分，而编程环境特性与语言特性同等重要，这些环境特性包括：库、工具、惯例和针对软件工程的整体做法，它们都对使用Go语言编程提供了支持，不可或缺。而这些环境特性恰恰是在传统的编程语言设计中并没有受到应有的重视的。 这样的决策让Go在开源之初就为开发者提供了使用Go进行编程所需的几乎一切：包括功能丰富、开箱即用的标准库以及全面的工具集，代码格式化、代码静态检查、依赖关系管理、构建(包括跨平台交叉编译)、测试、性能剖析、查看和生成文档等，并且这些工具集在今天都统一放在了go命令的下面。这个决策也帮助Go在开源后吸引了第一批Go社区用户。 Go的一致性的表现 Go的一个目标是让它在不同的实现、执行环境中，甚至在不同的时间内表现出相同的行为。所以，Go语言尽可能地规定了一致的结果。比如：Go程序生命周期内一致的性能(相对于使用JIT慢启动的语言）、一致的GC的开销等。甚至对于最常见的编程错误提供了明确定义的语义，这有助于可理解性和调试，而不是像C/C++中那样，充斥着各种未定义的行为。 而我认为最重要的一致性则是从2012年发布的Go 1.0开始，Go团队公开承诺只对语言和标准库进行向后兼容的修改，这样程序在编译到较新的Go版本时可以继续运行而不发生变化。这一承诺对业界产生了吸引力，它不仅鼓励了那些长声明周期的工程项目(比如Google内部的一些大型项目或者像Kubernetes这样的社区顶级项目)，也鼓励了其他努力，如书籍、培训课程和第三方软件包的繁荣生态系统。这一一致性的决策也为Go招募了相当数量的拥趸。 Go1兼容性，同样可以避免社区分裂（像python2/python3那样），即便是10多年来变更最大的泛型语法落地，也没有违反Go1兼容性，这实属不易。 b) 设计哲学 上述的设计决策的背后蕴含着Go语言之父们的设计哲学。 简单 Tony Hoare在1980年图灵奖演讲中说了这样的观点：“我的结论是，构建软件设计有两种方法：一种方法是让它变得如此简单，显然没有缺陷，另一种方法是让它变得如此复杂，以至于没有明显的缺陷。第一种方法要困难得多。它需要同样的技能，奉献，洞察力，甚至灵感，就像发现作为自然复杂现象基础的简单物理定律一样。它还要求愿意接受受物理，逻辑和技术限制的目标，并在无法实现冲突目标时接受妥协。” Go选择的正是Tony Hoare演进中的第一种构建软件的设计方法。Rob Pike说过的一句Go流行谚语“less is exponentially more”与此异曲同工。Go的语法简单，API简单，这些为Gopher提供了极大便利，但这些简单的背后其实是Go团队长时间的复杂的思考与实现，努力将语法和API简化为最小、最有用、最接近本质的努力工作。 同时，简单意味着可读性、可维护性，意味着代码的清晰。另一句Go谚语“Clear is better than clever”告诫Gopher们编写平淡如水的Go代码才是“政治正确”的，不要炫技。 并发 多核时代，Go将并发作为语言内置特性。Go内置并发原语，包括goroutine、channel、select等。 [...]]]></description>
			<content:encoded><![CDATA[<p><a href="https://tonybai.com/2022/12/07/why-go-succeed">本文永久链接</a> &#8211; https://tonybai.com/2022/12/07/why-go-succeed</p>
<p><img src="https://tonybai.com/wp-content/uploads/advanced-go/why-go-succeed-1.png" alt="" /></p>
<hr />
<p>大家在入门Go语言时，多埋头于Go语法，忙于练手或快速完成公司的项目，无暇思考。</p>
<p>但当大家到了要进阶，要冲刺高级阶段的时候，我建议你不能再稀里糊涂了。既然入了Go这个坑，在进入高级阶段前，我们最好在门口的“影壁墙”前驻留一下。</p>
<p><img src="https://tonybai.com/wp-content/uploads/advanced-go/why-go-succeed-2.jpg" alt="" /></p>
<p>仔细思考一下<strong>我们投入这么多精力研究的Go为什么能成功</strong>，后续还能否持续成功下去。你要有自己的基本的判断，自我暗示也好，坚定信心也罢，<strong>我们要为继续攀登Go高峰进行蓄能</strong>。</p>
<h2>一. 头脑风暴一下Go成功的因素</h2>
<p>相信无论针对哪个gopher群体做头脑风暴，让大家列举Go成功的因素，大家的主流答案也无外乎下图中这些：</p>
<p><img src="https://tonybai.com/wp-content/uploads/advanced-go/why-go-succeed-3.png" alt="" /></p>
<p>图中的各个因素与Go的成功都不无干系，但是究竟哪个或哪几个是决定性的呢？</p>
<h2>二. Go成功的根本因素</h2>
<p>很显然，这个问题是没有标准答案，是见仁见智的。这里我列举一下我的观点，供大家参考。</p>
<p>直接上结论，我认为<strong>Go成功的根本因素就一个：Google</strong>。</p>
<p>为什么这么说呢？下面我们展开来看(见下图)！</p>
<p><img src="https://tonybai.com/wp-content/uploads/advanced-go/why-go-succeed-4.png" alt="" /></p>
<p>我将Go社区比做一支军队，而Go就是Go社区的武器，与其他编程语言搏杀，占地盘(fans)。下面我们就来解构一下这支军队的构成以及为什么这支军队目前有诸多成功案例！</p>
<h3>1. Google为Go社区提供了统帅与武器</h3>
<p>众所周知，2007年Google的三名员工Robert Griesemer、Rob Pike和Ken Thompson(retire很早，精神上领袖，给予Go名誉上的背书)一起发明了Go语言，2009年Go开源后，Go社区逐渐形成。统帅是一支军队的灵魂，他们做出了影响Go和Go社区的最初的也是最重要的决策和这背后的Go设计哲学！</p>
<h4>a) 设计决策</h4>
<p>在2022年，Go团队在<a href="https://cacm.acm.org/">美国计算机学会通讯(Communications of the ACM)</a>期刊上发表paper：《<a href="https://tonybai.com/2022/05/04/the-paper-of-go-programming-language-and-environment">Go编程语言与环境</a>》，对当年做出的诸多决策做了细致说明，这里对其中两个最重要的决策做简单说明：</p>
<ul>
<li>Go旨在成为一个编程环境</li>
</ul>
<p>Go语言之父们认为语言特性仅是编程语言的一部分，而编程环境特性与语言特性同等重要，这些环境特性包括：库、工具、惯例和针对软件工程的整体做法，它们都对使用Go语言编程提供了支持，不可或缺。而这些环境特性恰恰是在传统的编程语言设计中并没有受到应有的重视的。</p>
<p>这样的决策让Go在开源之初就为开发者提供了使用Go进行编程所需的几乎一切：包括功能丰富、开箱即用的标准库以及全面的工具集，代码格式化、代码静态检查、依赖关系管理、构建(包括跨平台交叉编译)、测试、性能剖析、查看和生成文档等，并且这些工具集在今天都统一放在了go命令的下面。这个决策也帮助Go在开源后吸引了第一批Go社区用户。</p>
<ul>
<li>Go的一致性的表现</li>
</ul>
<p>Go的一个目标是<strong>让它在不同的实现、执行环境中，甚至在不同的时间内表现出相同的行为</strong>。所以，Go语言尽可能地规定了一致的结果。比如：Go程序生命周期内一致的性能(相对于使用JIT慢启动的语言）、一致的GC的开销等。甚至对于最常见的编程错误提供了明确定义的语义，这有助于可理解性和调试，而不是像C/C++中那样，充斥着各种未定义的行为。</p>
<p>而我认为最重要的一致性则是从2012年发布的Go 1.0开始，Go团队<strong>公开承诺只对语言和标准库进行向后兼容的修改</strong>，这样程序在编译到较新的Go版本时可以继续运行而不发生变化。这一承诺对业界产生了吸引力，它不仅鼓励了那些长声明周期的工程项目(比如Google内部的一些大型项目或者像Kubernetes这样的社区顶级项目)，也鼓励了其他努力，如书籍、培训课程和第三方软件包的繁荣生态系统。这一一致性的决策也为Go招募了相当数量的拥趸。 Go1兼容性，同样可以避免社区分裂（像python2/python3那样），即便是<a href="https://tonybai.com/2022/04/20/some-changes-in-go-1-18">10多年来变更最大的泛型语法落地</a>，也没有违反Go1兼容性，这实属不易。</p>
<h4>b) 设计哲学</h4>
<p>上述的设计决策的背后蕴含着Go语言之父们的设计哲学。</p>
<ul>
<li>简单</li>
</ul>
<p>Tony Hoare在1980年图灵奖演讲中说了这样的观点：“我的结论是，构建软件设计有两种方法：一种方法是让它变得如此简单，显然没有缺陷，另一种方法是让它变得如此复杂，以至于没有明显的缺陷。第一种方法要困难得多。它需要同样的技能，奉献，洞察力，甚至灵感，就像发现作为自然复杂现象基础的简单物理定律一样。它还要求愿意接受受物理，逻辑和技术限制的目标，并在无法实现冲突目标时接受妥协。”</p>
<p>Go选择的正是Tony Hoare演进中的第一种构建软件的设计方法。Rob Pike说过的一句Go流行谚语<a href="https://commandcenter.blogspot.com/2012/06/less-is-exponentially-more.html">“less is exponentially more”</a>与此异曲同工。Go的语法简单，API简单，这些为Gopher提供了极大便利，但这些简单的背后其实是Go团队长时间的复杂的思考与实现，努力将语法和API简化为最小、最有用、最接近本质的努力工作。</p>
<p>同时，简单意味着可读性、可维护性，意味着代码的清晰。另一句Go谚语“Clear is better than clever”告诫Gopher们编写平淡如水的Go代码才是“政治正确”的，不要炫技。</p>
<ul>
<li>并发</li>
</ul>
<p>多核时代，Go将并发作为语言内置特性。Go内置并发原语，包括goroutine、channel、select等。</p>
<p>Go鼓励在较高级别使用并发性，特别是通过通信的方式。我们耳熟能详的一句Go谚语是“Don&#8217;t communicate by sharing memory. Share memory by communicating”就是并发哲学的外在体现。</p>
<ul>
<li>组合 </li>
</ul>
<p>Go拥有类型，类型可以有method，这似乎像是一种面向对象style的实现，但Go并没有OO语言那种类型层次体系(type hierarchy)，在Go中，组合才是Go类型之间建立联系的最主要手段，而interface和类型嵌入恰是这种组合哲学的具体体现。</p>
<ul>
<li>面向工程</li>
</ul>
<p>2012年, Go开源元年，Rob Pike就在SPLASH 2012大会上以<a href="https://go.dev/talks/2012/splash.article">“Google的Go：为软件工程服务的语言设计”</a>为题，讲解了Go是如何围绕Google内部存在的软件工程问题进行有针对性的语言设计的。可以看出，Go从诞生伊始就将解决软件工程领域问题作为语言的目标。同时，我们看到面向工程这个哲学与上面的旨在成为一个编程环境的决策息息相关。</p>
<p>除了统帅之外，Go社区的治理架构也是以Google“将领”为核心的，我们继续来看。</p>
<h3>2. Google出钱：以Google“将领”(googler and ex-googler)为核心的Go社区治理架构</h3>
<p>Go开源10年了，Go社区形成了以Googler和ex-googler(前google员工)为核心的Go社区治理架构，这些人就是上图中的那些“将领”，他们是Go项目某个细分领域，比如：编译器、运行时goroutine调度、GC、内存管理、网络、安全等的领头人。根据Go项目一名产品经理的描述：<a href="https://tonybai.com/2022/01/16/the-2021-review-of-go-programming-language">2021年，Google Go项目的专职人员多达50多人</a>，Google这个“亲爹”在金钱的投入上显然表现的十分大方，不得不承认：<a href="https://tonybai.com/2012/10/08/the-new-age-of-programming-language/">在编程语言领域里，有个有钱的“亲爹”就是好</a>。</p>
<p>这种以googler和Ex-googler为开源社区治理核心的架构决定了Go社区采用的是一种我称之为“民主集中制”的决策机制。在Go社区你不要幻想会有绝对的公平投票，Go项目决策向来是由少数Googler和ex-googler主导的。这样意味着很多情况下，核心治理团队的人提出的proposal以及Google内部gopher提出proposal很容易被accept，而来自外部社区的proposal要想被accept，可能难度就要大一些。怎么说呢？Google的方案不一定总是最好的，但我们也不能不承认多数情况下，Googler提的proposal还是更优的，并且通常这些proposal对应的实现都已经在google内部测试过了，甚至和Go决策组在公司内部“吹过风”，如果你是Go社区的决策人，你会怎么做呢？你是更相信Googler还是外部一个没有任何背景的gopher呢？</p>
<p>我觉得在Google依然引领IT前沿的今天以及未来若干年，这种机制可能还是有利于Go的蓬勃发展的。</p>
<h3>3. Google为Go社区提供战场/试验场</h3>
<p>就像上面所说的那样，Go是有着非常鲜明Google烙印的编程语言，除了Go语言之父都来自google，Go社区治理架构的核心都来自Google和前google员工外，<strong>Google内部为Go的设计提供了足够的一流的问题域，也为Go的真实应用提供了试验场和真实战场</strong>，即便Go至今没有成为Google内部的第一语言。面向Google的一手且一流问题域，让Go设计者和Go开发者能够获得一手的反馈，从而对Go做进一步的打磨。</p>
<p>举几个例子：</p>
<ul>
<li>Google内部的单一代码仓库让Go最初设计了不带版本的go get(后在社区的强烈要求下引入了<a href="https://tonybai.com/2018/11/19/some-changes-in-go-1-11">go module</a>，go get才支持版本号)；</li>
<li>googler反馈，google内部工具超好用，这一定程度让Go团队认识到向Gopher提供完善的go工具链的重要性；</li>
<li>Google内部的多核与网络服务让Go设计者决定内置原生goroutine以应对多核时代的应用开发；</li>
<li>Google内性能与开发效率并重让Go设计者决定设计一门带gc的静态编程语言，将内存管理、并发管理下沉到runtime，这与近两年出现的服务网格, dapr等概念的思路一致；</li>
</ul>
<p><img src="https://tonybai.com/wp-content/uploads/advanced-go/why-go-succeed-5.png" alt="" /></p>
<ul>
<li>Google内部大规模人员协作让Go决定面向软件工程，不仅要设计好语言特性，还要提供体验良好的编程环境(工具链、标准库等)；</li>
<li>Google超大规模的系统构建慢让Go决定提供快速的构建能力，为此对包格式与包依赖做了精心的设计；</li>
<li>Google内部长期维护的系统(生命周期长) 让Go团队决定支持Go1兼容性并提供支持重构的语法，比如type alias等；</li>
<li>Google认为安全十分重要，促使Go提供了<a href="https://tonybai.com/2022/09/10/an-intro-of-govulncheck">go sumdb</a>和对<a href="https://tonybai.com/2022/03/14/software-supply-chain-security-in-go">sbom</a>的良好支持；</li>
</ul>
<p>同时Google内部系统为了支持Go的内部试验也是不遗余力，比如：每当Go发布大版本的RC版本，甚至是Beta版本时，Google App Engine都会首当其冲的充当“小白鼠”，在生产环境支持尚未发布正式版的Go。</p>
<p>另外Google在业内的领先性也让“近水楼台”的Go受益，比如像容器调度编排这样的平台，Google十年前就有了(borg)，后续Googler以另起开源项目的方式将其中经验外溢输出，让<a href="https://tonybai.com/tag/kubernetes">Kubernetes</a>最终选择了Go作为开发语言，从而成为Go的最大的也是最典型的成功战例。</p>
<p>综上，我们看到Google对Go成功的决定性作用，这种作用可决不能被理解为简单的金钱上的支撑。</p>
<h2>三. Go语言演进历史</h2>
<p>进入Go高级阶段后，对Go语言的演化历史要知道，当然能做到如数家珍更佳，即便不能，也要能记住Go语言的主要演化历史：</p>
<ul>
<li>2007年9月，Go语言诞生；</li>
<li>2009年11月，Go正式开源；</li>
<li>2012年3月，Go 1.0发布，同时Go1兼容性承诺官宣；</li>
<li>2014年12月，<a href="https://tonybai.com/2014/11/04/some-changes-in-go-1-4/">Go 1.4版本发布</a>，这是最后一个编译器和runtime由C语言实现的版本；</li>
<li>2015年8月，<a href="https://tonybai.com/2015/07/10/some-changes-in-go-1-5/">Go 1.5版本发布</a>，这个版本Go实现了自举(用go编译go)，同时编译器和runtime中的绝大部分c代码都换成了go，新版gc让延迟大幅降低；</li>
<li>2018年8月，<a href="https://tonybai.com/2018/11/19/some-changes-in-go-1-11">Go 1.11版本发布</a>，go module被正式引入；</li>
<li>2022年3月，<a href="https://tonybai.com/2022/04/20/some-changes-in-go-1-18">Go 1.18版本发布</a>，Go泛型语法正式落地。</li>
</ul>
<h2>四. 小结</h2>
<p>C++之父说过：“世上只有两种编程语言：一种是总是被人抱怨的，一种是从来没人用的”。</p>
<p>Go属于前者。世界上没有完美的编程语言，Go经过十年的打磨已经有了长足的进步，并且取得了不错的战绩，尤其是在云基础设施和云原生因公领域，就连Rob Pike也承认<a href="https://tonybai.com/2020/05/01/rob-pike-interview-go-become-the-language-of-cloud-infrastructure/">Go确实已成为云基础架构的语言</a>。而这个Go走向成功的过程中，Google起着根本性的作用。</p>
<p>不过中国古语有云：成也萧何，败也萧何！目前Google仍然引领IT技术前沿，这对Go的发展来说是一个利好，也会不断推动Go向着好的方向发展。</p>
<p>但我大胆预测一下：“成也Google，败也Google”，一旦Google开始走下坡路的那天，Go语言成功的根基就不在了，Go还能像今天这样顺风顺水么？如果Go社区治理结构不重构，很可能不会再有今天这样的良好状态。大家觉得呢？</p>
<h2>五. 参考资料</h2>
<p>-《<a href="https://tonybai.com/2022/05/04/the-paper-of-go-programming-language-and-environment">Go编程语言与环境：万字长文复盘导致Go语言成功的那些设计决策</a>》<br />
-《Go内存模型》- https://research.swtch.com/gomm<br />
-《Go语言真正的问题》 &#8211; https://vanitynotes.com/posts/20221101-the-real-problem-with-go</p>
<hr />
<p><a href="https://wx.zsxq.com/dweb2/index/group/51284458844544">“Gopher部落”知识星球</a>旨在打造一个精品Go学习和进阶社群！高品质首发Go技术文<br />
章，“三天”首发阅读权，每年两期Go语言发展现状分析，每天提前1小时阅读到新鲜的Gopher日报，网课、技术专栏、图书内容前瞻，六小时内必>答保证等满足你关于Go语言生态的所有需求！2022年，Gopher部落全面改版，将持续分享Go语言与Go应用领域的知识、技巧与实践，并增加诸多互<br />
动形式。欢迎大家加入！</p>
<p><img src="http://image.tonybai.com/img/tonybai/gopher-tribe-zsxq-small-card.png" alt="img{512x368}" /><br />
<img src="http://image.tonybai.com/img/tonybai/go-programming-from-beginner-to-master-qr.png" alt="img{512x368}" /></p>
<p><img src="http://image.tonybai.com/img/tonybai/go-first-course-banner.png" alt="img{512x368}" /><br />
<img src="http://image.tonybai.com/img/tonybai/imooc-go-column-pgo-with-qr.jpg" alt="img{512x368}" /></p>
<p>Gopher Daily(Gopher每日新闻)归档仓库 &#8211; https://github.com/bigwhite/gopherdaily</p>
<p>我的联系方式：</p>
<ul>
<li>微博(暂不可用)：https://weibo.com/bigwhite20xx</li>
<li>微博2：https://weibo.com/u/6484441286</li>
<li>博客：tonybai.com</li>
<li>github: https://github.com/bigwhite</li>
</ul>
<p><img src="http://image.tonybai.com/img/tonybai/iamtonybai-wechat-qr.png" alt="" /></p>
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。</p>
<p style='text-align:left'>&copy; 2022, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2022/12/07/why-go-succeed/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Go 1.20新特性前瞻</title>
		<link>https://tonybai.com/2022/11/17/go-1-20-foresight/</link>
		<comments>https://tonybai.com/2022/11/17/go-1-20-foresight/#comments</comments>
		<pubDate>Thu, 17 Nov 2022 13:45:59 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[arena]]></category>
		<category><![CDATA[cache]]></category>
		<category><![CDATA[Cgo]]></category>
		<category><![CDATA[coverage]]></category>
		<category><![CDATA[crypto]]></category>
		<category><![CDATA[err]]></category>
		<category><![CDATA[error]]></category>
		<category><![CDATA[fmt]]></category>
		<category><![CDATA[GC]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[go-build]]></category>
		<category><![CDATA[go-install]]></category>
		<category><![CDATA[go.dev]]></category>
		<category><![CDATA[Go1]]></category>
		<category><![CDATA[go1.18]]></category>
		<category><![CDATA[go1.19]]></category>
		<category><![CDATA[go1.20]]></category>
		<category><![CDATA[Go2]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[GOROOT]]></category>
		<category><![CDATA[gotest]]></category>
		<category><![CDATA[gRPC]]></category>
		<category><![CDATA[Interface]]></category>
		<category><![CDATA[library]]></category>
		<category><![CDATA[optimization]]></category>
		<category><![CDATA[optimize]]></category>
		<category><![CDATA[panic]]></category>
		<category><![CDATA[PGO]]></category>
		<category><![CDATA[pkg]]></category>
		<category><![CDATA[playground]]></category>
		<category><![CDATA[pprof]]></category>
		<category><![CDATA[proposal]]></category>
		<category><![CDATA[protobuf]]></category>
		<category><![CDATA[RFC3339]]></category>
		<category><![CDATA[runtime]]></category>
		<category><![CDATA[Slice]]></category>
		<category><![CDATA[stdlib]]></category>
		<category><![CDATA[strings]]></category>
		<category><![CDATA[template]]></category>
		<category><![CDATA[time]]></category>
		<category><![CDATA[unit-test]]></category>
		<category><![CDATA[Unwrap]]></category>
		<category><![CDATA[wrap]]></category>
		<category><![CDATA[优化]]></category>
		<category><![CDATA[内存管理]]></category>
		<category><![CDATA[切片]]></category>
		<category><![CDATA[发行版]]></category>
		<category><![CDATA[垃圾回收]]></category>
		<category><![CDATA[工具链]]></category>
		<category><![CDATA[数组]]></category>
		<category><![CDATA[标准库]]></category>
		<category><![CDATA[泛型]]></category>
		<category><![CDATA[编译器]]></category>
		<category><![CDATA[语法]]></category>
		<category><![CDATA[运行时]]></category>
		<category><![CDATA[链接器]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=3725</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2022/11/17/go-1-20-foresight 在近期Russ Cox代表Go核心团队发表的“Go, 13周年”一文中，他提到了“在Go的第14个年头，Go团队将继续努力使Go成为用于大规模软件工程的最好的环境，将特别关注供应链安全，提高兼容性和结构化日志记录，当然还会有很多其他改进，包括profile-guided optimization等”。 当前正在开发的版本是Go 1.20，预计2023年2月正式发布，这个版本也将是Go在其第14个年头发布的第一个版本。很多人没想到Go真的会进入到Go 1.2x版本，而不是Go 2.x。记得Russ Cox曾说过可能永远也不会有Go2了，毕竟Go泛型语法落地这么大的语法改动也没有让Go1兼容性承诺失效。 目前Go 1.20版本正在如火如荼的开发中，很多gopher都好奇Go 1.20版本会带来哪些新特性？在这篇文章中，我就带大家一起去Go 1.20 milestone的issues列表中翻翻，提前看看究竟会有哪些新特性加入Go。 1. 语法变化 Go在其1.18版本迎来了自开源以来最大规模的语法变化，然后呢？就没有然后了。Go在语法演进上再次陷入沉寂，没错，这就是Go长期以来坚持的风格。 如果Go 1.20版本真有语法层面的变化，那估计就是这个issue了：“spec: allow conversion from slice to array”，即允许切片类型到数组类型的类型转换。 在Go 1.20版本之前，我们以Go 1.19版本为例写下下面代码： package main import "fmt" func main() { var sl = []int{1, 2, 3, 4, 5, 6, 7} var arr = [7]int(sl) // 编译器报错：cannot convert [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/go-1-20-foresight-1.png" alt="" /></p>
<p><a href="https://tonybai.com/2022/11/17/go-1-20-foresight">本文永久链接</a> &#8211; https://tonybai.com/2022/11/17/go-1-20-foresight</p>
<hr />
<p>在近期Russ Cox代表Go核心团队发表的<a href="https://tonybai.com/2022/11/11/go-opensource-13-years">“Go, 13周年”</a>一文中，他提到了“在Go的第14个年头，Go团队将继续努力使Go成为用于大规模软件工程的最好的环境，将特别关注供应链安全，提高兼容性和结构化日志记录，当然还会有很多其他改进，包括profile-guided optimization等”。</p>
<p>当前正在开发的版本是Go 1.20，预计2023年2月正式发布，这个版本也将是Go在其第14个年头发布的第一个版本。很多人没想到Go真的会进入到Go 1.2x版本，而不是Go 2.x。记得Russ Cox曾说过可能永远也不会有Go2了，毕竟<a href="https://tonybai.com/2022/04/20/some-changes-in-go-1-18">Go泛型语法落地</a>这么大的语法改动也没有让<a href="https://go.dev/doc/go1compat">Go1兼容性承诺</a>失效。</p>
<p>目前Go 1.20版本正在如火如荼的开发中，很多gopher都好奇Go 1.20版本会带来哪些新特性？在这篇文章中，我就带大家一起去<a href="https://github.com/golang/go/milestone/250">Go 1.20 milestone</a>的issues列表中翻翻，提前看看究竟会有哪些新特性加入Go。</p>
<h3>1. 语法变化</h3>
<p>Go在其<a href="https://tonybai.com/2022/04/20/some-changes-in-go-1-18">1.18版本</a>迎来了自开源以来最大规模的语法变化，然后呢？就没有然后了。Go在语法演进上再次陷入沉寂，没错，这就是Go长期以来坚持的风格。</p>
<p>如果Go 1.20版本真有语法层面的变化，那估计就是这个issue了：<a href="https://github.com/golang/go/issues/46505">“spec: allow conversion from slice to array”</a>，即<strong>允许切片类型到数组类型的类型转换</strong>。</p>
<p>在Go 1.20版本之前，我们以<a href="https://tonybai.com/2022/08/22/some-changes-in-go-1-19">Go 1.19版本</a>为例写下下面代码：</p>
<pre><code>package main

import "fmt"

func main() {
    var sl = []int{1, 2, 3, 4, 5, 6, 7}
    var arr = [7]int(sl) // 编译器报错：cannot convert sl (variable of type []int) to type [7]int
    fmt.Println(sl)
    fmt.Println(arr)
}
</code></pre>
<p>这段代码中，我们进行了一个[]int到[7]int的类型转换，但在Go 1.19版本编译器针对这个转换会报错！即不支持将切片类型显式转换数组类型。</p>
<p>在Go 1.20版本之前如果要实现切片到数组的转换，是有trick的，看下面代码：</p>
<pre><code>func main() {
    var sl = []int{1, 2, 3, 4, 5, 6, 7}
    var parr = (*[7]int)(sl)
    var arr = *(*[7]int)(sl)
    fmt.Println(sl)  // [1 2 3 4 5 6 7]
    fmt.Println(arr) // [1 2 3 4 5 6 7]
    sl[0] = 11
    fmt.Println(sl)    // [11 2 3 4 5 6 7]
    fmt.Println(arr)   // [1 2 3 4 5 6 7]
    fmt.Println(*parr) // [11 2 3 4 5 6 7]
}
</code></pre>
<p>该trick的理论基础是Go允许获取切片的底层数组地址。在上面的例子中parr就是指向切片sl底层数组的指针，通过sl或parr对底层数组元素的修改都能在对方身上体现出来。但是arr则是底层数组的一个副本，后续通过sl对切片的修改或通过parr对底层数组的修改都不会影响arr，反之亦然。</p>
<p>不过这种trick语法还不是那么直观！于是上面那个“允许将切片直接转换为数组”的issue便提了出来。我们在<a href="https://go.dev/play">go playground</a>上选择“go dev branch”便可以使用最新go tip的代码，我们尝试一下最新语法：</p>
<pre><code>func main() {
    var sl = []int{1, 2, 3, 4, 5, 6, 7}
    var arr = [7]int(sl)
    var parr = (*[7]int)(sl)
    fmt.Println(sl)   // [1 2 3 4 5 6 7]
    fmt.Println(arr)  // [1 2 3 4 5 6 7]
    sl[0] = 11
    fmt.Println(arr)  // [1 2 3 4 5 6 7]
    fmt.Println(parr) // &amp;[11 2 3 4 5 6 7]
}
</code></pre>
<p>我们看到直接将sl转换为数组arr不再报错，但其语义与前面的“var arr = <em>(</em>[7]int)(sl)”语义是相同的，即返回一个切片底层数组的副本，arr不会受到后续切片元素变化的影响。</p>
<p>不过这里也有个约束，那就是转换后的数组长度要小于等于切片长度，否则会panic：</p>
<pre><code>var sl = []int{1, 2, 3, 4, 5, 6, 7}
var arr = [8]int(sl) // panic: runtime error: cannot convert slice with length 7 to array or pointer to array with length 8
</code></pre>
<p>在写本文时，该issue尚未close，不过进入最终Go 1.20版本应该不是大问题。</p>
<h3>2. 编译器/链接器和其他工具链</h3>
<h4>1) profile-guided optimization</h4>
<p>Go编译器团队一直致力于对Go编译器/链接器的优化，这次在Go 1.20版本中，该团队很大可能会给我们带来<a href="https://github.com/golang/proposal/blob/master/design/55022-pgo.md">“profile-guided optimization”</a>。</p>
<p>什么是“profile-guided optimization”呢？原先Go编译器实施的优化手段，比如<a href="https://tonybai.com/2022/10/17/understand-go-inlining-optimisations-by-example">内联</a>，都是基于固定规则决策的，所有信息都来自编译的Go源码。而这次的<a href="https://github.com/golang/go/issues/55022">“profile-guided optimization”</a>顾名思义，需要源码之外的信息做“制导”来决定实施哪些优化，这个源码之外的信息就是profile信息，即来自pprof工具在程序运行时采集的数据，如下图(图来自<a href="https://github.com/golang/proposal/blob/master/design/55022-pgo-implementation.md">profile-guided optimization设计文档</a>)所示:</p>
<p><img src="https://tonybai.com/wp-content/uploads/go-1-20-foresight-2.png" alt="" /></p>
<p>因此pgo优化实际上是需要程序员参与的，程序员拿着程序到生产环境跑，程序生成的profile性能采集数据会被保存下来，然后这些profile采集数据会提供给Go编译器，以在下次构建同一个程序时辅助优化决策。由于这些profile是来自生产环境或模拟生产环境的数据，使得这种优化更有针对性。并且，Google数据中心其他语言(C/C++)实施PGO优化的效果显示，优化后的性能保守估计提升幅度在5%~15%。</p>
<p>和其他新引入的特性一样，Go 1.20将包含该特性，但默认并不开启，我们可以手动开启进行体验，未来版本，pgo特性才会默认为auto开启。</p>
<h4>2) 大幅减小Go发行版包的Size</h4>
<p>随着Go语言的演进，Go发行版的Size也在不断增加，从最初的几十M到如今的上百M。本地电脑里多安装几个Go版本，(解压后)几个G就没有了，此外Size大也让下载时间变得更长，尤其是一些网络环境不好的地区。</p>
<p>为什么Go发行版Size越来越大呢？这很大程度是因为Go发行版中包含了GOROOT下所有软件包的预编译.a文件，以go 1.19的macos版本为例，在\$GOROOT/pkg下，我们看到下面这些.a文件，用du查看一下占用的磁盘空间，达111M：</p>
<pre><code>$ls
archive/    database/   fmt.a       index/      mime/       plugin.a    strconv.a   time/
bufio.a     debug/      go/     internal/   mime.a      reflect/    strings.a   time.a
bytes.a     embed.a     hash/       io/     net/        reflect.a   sync/       unicode/
compress/   encoding/   hash.a      io.a        net.a       regexp/     sync.a      unicode.a
container/  encoding.a  html/       log/        os/     regexp.a    syscall.a   vendor/
context.a   errors.a    html.a      log.a       os.a        runtime/    testing/
crypto/     expvar.a    image/      math/       path/       runtime.a   testing.a
crypto.a    flag.a      image.a     math.a      path.a      sort.a      text/

$du -sh
111M    .
</code></pre>
<p>而整个pkg目录的size为341M，占Go 1.19版本总大小495M的近70%。</p>
<p>于是在Go社区提议下，<a href="https://github.com/golang/go/issues/47257">Go团队决定从Go 1.20开始发行版不再为GOROOT中的大多数软件包提供预编译的.a文件</a>，新版本将只包括GOROOT中使用cgo的几个软件包的.a文件。</p>
<p>因此Go 1.20版本中，GOROOT下的源码将像其他用户包那样在构建后被缓存到本机cache中。此外，go install也不会为GOROOT软件包安装.a文件，除非是那些使用cgo的软件包。这样Go发行版的size将最多减少三分之二。</p>
<p>取而代之的是，这些包将在需要时被构建并缓存在构建缓存中，就像已经为GOROOT之外的非主包所做的那样。此外，go install也不会为GOROOT软件包安装.a文件，除非是那些使用cgo的软件包。这些改变是为了减少Go发行版的大小，在某些情况下可以减少三分之二。</p>
<h4>3) 扩展代码覆盖率(coverage)报告到应用本身</h4>
<p>想必大家都用过go test的输出过代码覆盖率，go test会在unit test代码中<strong>注入代码</strong>以统计unit test覆盖的被测试包路径，下面是代码注入的举例：</p>
<pre><code>func ABC(x int) {
    if x &lt; 0 {
        bar()
    }
}
</code></pre>
<p>注入代码后：</p>
<pre><code>func ABC(x int) {GoCover_0_343662613637653164643337.Count[9] = 1;
  if x &lt; 0 {GoCover_0_343662613637653164643337.Count[10] = 1;
    bar()
  }
}
</code></pre>
<p>像GoCover_xxx这样的代码会被放置到每条分支路径下。</p>
<p>不过go test -cover也有一个问题，那就是它只是适合针对包收集数据并提供报告，它无法针对应用整体给出代码覆盖度报告。</p>
<p>Go 1.20版本中有关的<a href="https://github.com/golang/proposal/blob/master/design/51430-revamp-code-coverage.md">“extend code coverage testing to include applications”</a>的proposal就是来扩展代码覆盖率的，可以支持对应用整体的覆盖率统计和报告。</p>
<p>该特性在Go 1.20版本中也将作为实验性特性，默认是off的。该方案通过go build -cover方式生成注入了覆盖率统计代码的应用程序，在应用执行过程中，报告会被生成到指定目录下，我们依然可以通过go tool cover来查看这个整体性报告。</p>
<p>此外，新proposal在实现原理上与go test -cover差不多，都是source-to-source的方案，这样后续也可以统一维护。当然Go编译器也会有一些改动。</p>
<h4>4) 废弃-i flag</h4>
<p><a href="https://github.com/golang/go/issues/41696">这个是一个早计划好的“废弃动作”</a>。自从Go 1.10引入go build cache后，go build/install/test -i就不会再将编译好的包安装到\$GOPATH/pkg下面了。</p>
<h3>3. Go标准库</h3>
<h4>1) 支持wrap multiple errors</h4>
<p>Go 1.20<a href="https://github.com/golang/go/issues/53435#issuecomment-1191752789">增加了一种将多个error包装(wrap)为一个error的机制</a>，方便从打包后的错误的Error方法中一次性得到包含一系列关于该错误的相关错误的信息。</p>
<p>这个机制增加了一个(匿名)接口和一个函数：</p>
<pre><code>interface {
    Unwrap() []error
}

func Join(errs ...error) error
</code></pre>
<p>同时增强了像fmt.Errorf这样的函数的语义，当在Errorf中使用多个%w verb时，比如：</p>
<pre><code>e := errors.Errorf("%w, %w, %w", e1, e2, e3)
</code></pre>
<p>Errorf将返回一个将e1, e2, e3打包完的且实现了上述带有Unwrap() &#91;&#93;error方法的接口的错误类型实例。</p>
<p>Join函数的语义是将传入的所有err打包成一个错误类型实例，该实例同样实现了上述带有Unwrap() &#91;&#93;error方法的接口，且该错误实例的类型的Error方法会返回换行符间隔的错误列表。</p>
<p>我们看一下下面这个例子：</p>
<pre><code>package main

import (
    "errors"
    "fmt"
)

type MyError struct {
    s string
}

func (e *MyError) Error() string {
    return e.s
}

func main() {
    e1 := errors.New("error1")
    e2 := errors.New("error2")
    e3 := errors.New("error3")
    e4 := &amp;MyError{
        s: "error4",
    }
    e := fmt.Errorf("%w, %w, %w, %w", e1, e2, e3, e4)

    fmt.Printf("e = %s\n", e.Error()) // error1 error2, error3, error4
    fmt.Println(errors.Is(e, e1)) // true

    var ne *MyError
    fmt.Println(errors.As(e, &amp;ne)) // true
    fmt.Println(ne == e4) // true
}
</code></pre>
<p>我们首先在Go 1.19编译运行上面程序：</p>
<pre><code>e = error1 %!w(*errors.errorString=&amp;{error2}), %!w(*errors.errorString=&amp;{error3}), %!w(*main.MyError=&amp;{error4})
false
false
false
</code></pre>
<p>显然Go 1.19的fmt.Errorf函数尚不支持多%w verb。</p>
<p>而Go 1.20编译上面程序的运行结果为：</p>
<pre><code>e = error1 error2, error3, error4
true
true
true
</code></pre>
<p>将fmt.Errorf一行换为：</p>
<pre><code>e := errors.Join(e1, e2, e3, e4)
</code></pre>
<p>再运行一次的结果为：</p>
<pre><code>e = error1
error2
error3
error4
true
true
true
</code></pre>
<p>即Join函数打包后的错误类型实例类型的Error方法会返回换行符间隔的错误列表。</p>
<h4>2) 新增arena实验包</h4>
<p>Go是带GC语言，虽然Go GC近几年持续改进，绝大多数场合都不是大问题了。但是在一些性能敏感的领域，GC过程占用的可观算力还是让应用吃不消。</p>
<p>降GC消耗，主要思路就是减少堆内存分配、减少反复的分配与释放。Go社区的某些项目为了减少内存GC压力，在mmaped内存上又建立一套GC无法感知到的简单内存管理机制并在适当场合应用。但这些自实现的、脱离GC的内存管理都有各自的问题。</p>
<p>Go 1.18版本发布前，<a href="https://github.com/golang/go/issues/51317">arena这个proposal</a>就被提上了日程，arena包又是google内部的一个实验包，据说效果还不错的(在改进grpc的protobuf反序列化实验上)，可以节省15%的cpu和内存消耗。但proposal一出，便收到了来自各方的comment，该proposal在Go 1.18和Go 1.19一度处于hold状态，直到Go 1.20才纳入到试验特性，我们可以通过GOEXPERIMENT=arena开启该机制。</p>
<p>arena包主要思路其实是“整体分配，零碎使用，再整体释放”，以最大程度减少对GC的压力。关于arena包，等进一步完善后，后续可能会有专门文章分析。</p>
<h4>3) time包变化</h4>
<p><a href="https://github.com/golang/go/issues/52746">time包增加了三个时间layout格式常量</a>，相信不用解释，大家也知道如何使用：</p>
<pre><code>    DateTime   = "2006-01-02 15:04:05"
    DateOnly   = "2006-01-02"
    TimeOnly   = "15:04:05"
</code></pre>
<p><a href="https://github.com/golang/go/issues/50770">time包还为Time增加了Compare方法</a>，适用于time之间的>=和&lt;=比较：</p>
<pre><code>// Compare returns -1 if t1 is before t2, 0 if t1 equals t2 or 1 if t1 is after t2.
func (t1 Time) Compare(t2 Time) int
</code></pre>
<p>此外，time包的RFC3339时间格式是使用最广泛的时间格式，<a href="https://github.com/golang/go/issues/50770">其解析性能在Go 1.20中得到优化，提升了70%左右，格式化性能提升30%</a>。</p>
<h3>4. 其他</h3>
<ul>
<li>Go 1.17版本将作为Go 1.20的bootstrap编译器;</li>
<li><a href="https://go-review.googlesource.com/c/go/+/432897">Go编译器性能提升3%</a>；</li>
<li>Go工具链将根据GO&#91;arch&#93;环境变量的设置<a href="https://github.com/golang/go/issues/45454">自动设置相关build tags</a>；</li>
<li>标准库<a href="https://github.com/golang/go/issues/52221">增加crypto/ecdh包</a>，提供安全的、基于byte切片的ECDH API；</li>
<li><a href="https://github.com/golang/go/issues/45038">bytes, strings包增加Clone函数</a>；</li>
<li><a href="https://github.com/golang/go/issues/42537">strings包增加CutPrefix和CutSuffix函数</a>；</li>
<li><a href="https://github.com/golang/go/issues/53261">text/template的解析性能提升40%</a>。</li>
</ul>
<h3>5. 参考资料</h3>
<ul>
<li>Go 1.20 milestone &#8211; https://github.com/golang/go/milestone/250</li>
<li>Exploring Go&#8217;s Profile-Guided Optimizations &#8211; https://www.polarsignals.com/blog/posts/2022/09/exploring-go-profile-guided-optimizations/</li>
<li>What&#8217;s coming to go 1.20 &#8211; https://twitter.com/mvdan_/status/1588242469577117696</li>
</ul>
<hr />
<p><a href="https://wx.zsxq.com/dweb2/index/group/51284458844544">“Gopher部落”知识星球</a>旨在打造一个精品Go学习和进阶社群！高品质首发Go技术文章，“三天”首发阅读权，每年两期Go语言发展现状分析，每天提前1小时阅读到新鲜的Gopher日报，网课、技术专栏、图书内容前瞻，六小时内必答保证等满足你关于Go语言生态的所有需求！2022年，Gopher部落全面改版，将持续分享Go语言与Go应用领域的知识、技巧与实践，并增加诸多互动形式。欢迎大家加入！</p>
<p><img src="http://image.tonybai.com/img/tonybai/gopher-tribe-zsxq-small-card.png" alt="img{512x368}" /><br />
<img src="http://image.tonybai.com/img/tonybai/go-programming-from-beginner-to-master-qr.png" alt="img{512x368}" /></p>
<p><img src="http://image.tonybai.com/img/tonybai/go-first-course-banner.png" alt="img{512x368}" /><br />
<img src="http://image.tonybai.com/img/tonybai/imooc-go-column-pgo-with-qr.jpg" alt="img{512x368}" /></p>
<p><a href="https://tonybai.com/">我爱发短信</a>：企业级短信平台定制开发专家 https://tonybai.com/。smspush : 可部署在企业内部的定制化短信平台，三网覆盖，不惧大并发接入，可定制扩展； 短信内容你来定，不再受约束, 接口丰富，支持长短信，签名可选。2020年4月8日，中国三大电信运营商联合发布《5G消息白皮书》，51短信平台也会全新升级到“51商用消息平台”，全面支持5G RCS消息。</p>
<p>著名云主机服务厂商DigitalOcean发布最新的主机计划，入门级Droplet配置升级为：1 core CPU、1G内存、25G高速SSD，价格5$/月。有使用DigitalOcean需求的朋友，可以打开这个<a href="https://m.do.co/c/bff6eed92687">链接地址</a>：https://m.do.co/c/bff6eed92687 开启你的DO主机之路。</p>
<p>Gopher Daily(Gopher每日新闻)归档仓库 &#8211; https://github.com/bigwhite/gopherdaily</p>
<p>我的联系方式：</p>
<ul>
<li>微博(暂不可用)：https://weibo.com/bigwhite20xx</li>
<li>微博2：https://weibo.com/u/6484441286</li>
<li>博客：tonybai.com</li>
<li>github: https://github.com/bigwhite</li>
</ul>
<p><img src="http://image.tonybai.com/img/tonybai/iamtonybai-wechat-qr.png" alt="" /></p>
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。</p>
<p style='text-align:left'>&copy; 2022, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2022/11/17/go-1-20-foresight/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>评点2021-2022年上市的那些Go语言新书</title>
		<link>https://tonybai.com/2022/06/01/reviewing-those-new-go-language-books-coming-out-in-2021-2022/</link>
		<comments>https://tonybai.com/2022/06/01/reviewing-those-new-go-language-books-coming-out-in-2021-2022/#comments</comments>
		<pubDate>Tue, 31 May 2022 21:29:35 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[618]]></category>
		<category><![CDATA[ANSI-C]]></category>
		<category><![CDATA[ANTLR]]></category>
		<category><![CDATA[GC]]></category>
		<category><![CDATA[generics]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[go1.18]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[goroutine]]></category>
		<category><![CDATA[Go语言定制指南]]></category>
		<category><![CDATA[Go语言底层原理剖析]]></category>
		<category><![CDATA[Go语言第一课]]></category>
		<category><![CDATA[Go语言精进之路]]></category>
		<category><![CDATA[Go语言设计与实现]]></category>
		<category><![CDATA[Go语言高级编程]]></category>
		<category><![CDATA[Grammar]]></category>
		<category><![CDATA[K&R]]></category>
		<category><![CDATA[map]]></category>
		<category><![CDATA[syntax]]></category>
		<category><![CDATA[TheGoProgrammingLanguage]]></category>
		<category><![CDATA[TheWayToGo]]></category>
		<category><![CDATA[书籍]]></category>
		<category><![CDATA[内存分配]]></category>
		<category><![CDATA[函数]]></category>
		<category><![CDATA[切片]]></category>
		<category><![CDATA[口碑]]></category>
		<category><![CDATA[垃圾回收]]></category>
		<category><![CDATA[字符串]]></category>
		<category><![CDATA[异常处理]]></category>
		<category><![CDATA[影印版]]></category>
		<category><![CDATA[微信读书]]></category>
		<category><![CDATA[接口]]></category>
		<category><![CDATA[数组]]></category>
		<category><![CDATA[文法]]></category>
		<category><![CDATA[泛型]]></category>
		<category><![CDATA[深入理解java虚拟机]]></category>
		<category><![CDATA[类型系统]]></category>
		<category><![CDATA[编译原理]]></category>
		<category><![CDATA[编译器]]></category>
		<category><![CDATA[解析器]]></category>
		<category><![CDATA[解释器]]></category>
		<category><![CDATA[词法分析]]></category>
		<category><![CDATA[语义分析]]></category>
		<category><![CDATA[语法]]></category>
		<category><![CDATA[语法分析]]></category>
		<category><![CDATA[语法树]]></category>
		<category><![CDATA[豆瓣]]></category>
		<category><![CDATA[运作机制]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=3574</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2022/06/01/reviewing-those-new-go-language-books-coming-out-in-2021-2022 计算机科学与技术这个工业大类与传统工业类别相比还很“年轻”，并且由于历史原因，整个计算机科学与技术学科的奠基都是由欧美人完成的，因此但凡诞生一门新IT技术或新编程语言，我们首先参考的都是来自欧美的外文技术书籍(影印或翻译)。 以Go为例，笔者最先接触的Go技术书籍资料是《The Way To Go》： 这也是笔者早期学习Go语言时最喜欢翻看的一本书，也是我目前见到的、最全面详实的讲解Go语言的书籍了，可以说是Gopher们的第一本“Go语言百科全书”。可能是由于这本书出版太早了，等国内出版社意识到要引进Go语言方面的书籍的时候，这本书使用的Go版本已经太老了。不过，这本书中绝大部分例子依然可以在今天最新的Go编译器下通过编译并运行起来。 另外一本不得不提的就是由K&#38;R C中的K：Brian W. Kernighan老爷子参与编写的《The Go Programming Language》： 这本书模仿并致敬《The C Programming Language》的经典结构，从一个”hello, world”示例开始带领大家开启Go语言之旅。作者行文十分精炼，字字珠玑，这与《The C Programming Language》的风格保持了高度一致。而且，书中的示例在浅显易懂的同时，又极具实用性，还突出Go语言的特点（比如并发web爬虫、并发非阻塞的缓存系统等）。读完这本书后，你会有一种爱不释手，马上还要从头再读一遍的感觉，这也许这就是“Go语言圣经”的魅力吧！ 不过，随着Go语言在国内的扎根和广泛应用，国内接纳Go较早的一批Gopher以及国内大厂“身经百战”的Gopher开始将Go语言沉淀下来，并陆续上线了自己的作品。从2020年开始，国内作者出版的Go语言相关书籍已经逐渐多了起来，并且质量也在逐渐提升。就像我在《Go语言第一课》 的加餐文章《我“私藏”的那些优质且权威的Go语言学习资料》中预测的那样：将有更多Gopher加入Go技术书籍的写作行列，从2021开始的3年，国内Go语言技术书籍也会迎来一波小高峰。 618购物节前夕，我就来简单评点一下2021年至今出版的口碑还不错的Go语言新书(按出版时间顺序)，大家可以趁打折力度较大的窗口按需从电商平台购买纸版书或电子书渠道购买电子书阅读^_^。 1. 《Go语言底层原理剖析》 2021.8 Go语言是带有GC与运行时的语言，这就意味着很多东西不是“表面”看到的那样，比如string、切片、map等类型在运行时的表示与我们在源码中看到的有很大不同。要想玩转Go语言，不下沉到“原理”这一层还真不行。 《Go语言底层原理剖析》这本书显然也是定位了那些对Go原理有述求的这部分gopher群体。书的作者郑建勋老师是滴滴的高级研发工程师。大家知道，滴滴公司内部使用Go技术栈实现的服务比例是很高的，因此这本书也是郑老师在滴滴“摸爬滚打”后的实践检验的沉淀与总结。 这本书从Go编译构建原理起步，然后过渡到Go的几种常见复合类型(数组、字符串、切片、map)的实现原理的讲解，再到对Go核心语法函数、接口、异常处理的原理说明，最后是Go的精华，也是最难啃的部分：goroutine调度、内存分配与GC。如果从覆盖的内容全面性上，应该说基本都包含到了。 笔者在微信读书上对整本书做了阅读，从阅读体验来看，郑老师的技术十分扎实，讲解也很到位。美中不足的是，有些内容刚刚引发你想继续深入的兴趣时，书籍内容却在这里戛然而止了。如果能继续展开就更好了，也许这是基于书籍篇幅上的考量。 ✩豆瓣评分：8.5 ✩微信读书推荐值：57.7% 本书在豆瓣口碑与微信读书推荐上存在一些分化，原因这个还不得而知。 2. 《Go语言设计与实现》 2021.11 《Go语言设计与实现》一书是作者左书祺(Draven)在其同名开源电子书《Go语言设计与实现》的基础上进一步系统整理和丰富而成。左老师的开源电子书在国内Gopher圈内有着相当好的口碑，他擅长以精美插图的方式对技术细节进行细致入微的讲解，作者甚至还专门出过一篇《技术文章配图指南》来说明其文章中插图制作使用的工具以及方法。 和《Go语言底层原理剖析》一样，《Go语言设计与实现》同样聚焦在Go编译器、类型系统与运行机制的原理层面，两本书对原理的说明角度和风格各有特点，就看读者喜欢哪种。更好的方法是主题阅读，两个相互参照的看。 编写面向Go底层原理的书是有一定“风险”的，很容易随着时间的流逝而变得“outdated”，这是因为Go语言还在快速演进中，其底层实现也在不断变化，远没有Java那样成熟，所以很难像神作《深入理解java虚拟机》那般“稳定”，需要不断更新。在这一点上，纸板书反倒没有开源电子书优势明显，后者可做到以快速持续的迭代更新。 不过笔者觉得：要想对一个语言机制的底层原理理解透彻，光是掌握其当前的实现机制还不够，了解其实现机制的历史演进过程将大有裨益，而上面的两本书的价值恰恰还可以体现在这个方面，尤其是当书中的实现机制在将来过时的时候。 ✩豆瓣评分：8.5 ✩微信读书推荐值：未上架 3. 《Go语言精进之路》 2021.12.17 写Go语言语法方面的书风险小，Go书籍的寿命都很长，这是因为Go1兼容性承诺的存在，这也是Go书籍作者的幸运。 《Go语言精进之路》是笔者的作品，该作品主要面向一个刚刚Go入门后的Go新手，就像副标题描述的那样，聚焦于告诉一个Go入门新手如何能像Go开发团队那样写出符合Go思维和语言惯例的高质量代码。书中也有一部分底层原理的介绍，但这些介绍也都是为了配合主线的讲解。由于是偏思维、方法与技巧方面的讲解，里面的绝大部分知识点，即使是几年后，依然是有效的。这就像出版于2015年的Go语言圣经《The Go Programming language》目前看毫不过时一样。 笔者自己的书不好自作点评，下面是近期一位读者在weibo上主动at我的评价： [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/reviewing-those-new-go-language-books-coming-out-in-2021-2022-1.png" alt="" /></p>
<p><a href="https://tonybai.com/2022/06/01/reviewing-those-new-go-language-books-coming-out-in-2021-2022">本文永久链接</a> &#8211; https://tonybai.com/2022/06/01/reviewing-those-new-go-language-books-coming-out-in-2021-2022</p>
<p>计算机科学与技术这个工业大类与传统工业类别相比还很“年轻”，并且由于历史原因，整个计算机科学与技术学科的奠基都是由欧美人完成的，因此但凡诞生一门新IT技术或新编程语言，我们首先参考的都是来自欧美的外文技术书籍(影印或翻译)。</p>
<p>以Go为例，笔者最先接触的Go技术书籍资料是<a href="https://book.douban.com/subject/10558892/">《The Way To Go》</a>：</p>
<p><img src="https://tonybai.com/wp-content/uploads/reviewing-those-new-go-language-books-coming-out-in-2021-2022-5.png" alt="" /></p>
<p>这也是笔者早期学习Go语言时最喜欢翻看的一本书，也是我目前见到的、最全面详实的讲解Go语言的书籍了，可以说是Gopher们的第一本“Go语言百科全书”。可能是由于这本书出版太早了，等国内出版社意识到要引进Go语言方面的书籍的时候，这本书使用的Go版本已经太老了。不过，这本书中绝大部分例子依然可以在今天最新的Go编译器下通过编译并运行起来。</p>
<p>另外一本不得不提的就是由K&amp;R C中的K：<a href="https://www.cs.princeton.edu/~bwk/">Brian W. Kernighan老爷子</a>参与编写的<a href="http://www.gopl.io">《The Go Programming Language》</a>：</p>
<p><img src="https://tonybai.com/wp-content/uploads/reviewing-those-new-go-language-books-coming-out-in-2021-2022-6.jpeg" alt="" /></p>
<p>这本书模仿并致敬<a href="http://en.wikipedia.org/wiki/The_C_Programming_Language">《The C Programming Language》</a>的经典结构，从一个”hello, world”示例开始带领大家开启Go语言之旅。作者行文十分精炼，字字珠玑，这与《The C Programming Language》的风格保持了高度一致。而且，书中的示例在浅显易懂的同时，又极具实用性，还突出Go语言的特点（比如并发web爬虫、并发非阻塞的缓存系统等）。读完这本书后，你会有一种爱不释手，马上还要从头再读一遍的感觉，这也许这就是“Go语言圣经”的魅力吧！</p>
<p>不过，随着<a href="https://tonybai.com/2022/01/16/the-2021-review-of-go-programming-language">Go语言在国内的扎根和广泛应用</a>，国内接纳Go较早的一批Gopher以及国内大厂“身经百战”的Gopher开始将Go语言沉淀下来，并陆续上线了自己的作品。从2020年开始，国内作者出版的Go语言相关书籍已经逐渐多了起来，并且质量也在逐渐提升。就像我在<a href="http://gk.link/a/10AVZ">《Go语言第一课》</a> 的加餐文章<a href="https://time.geekbang.org/column/article/468213">《我“私藏”的那些优质且权威的Go语言学习资料》</a>中预测的那样：<strong>将有更多Gopher加入Go技术书籍的写作行列，从2021开始的3年，国内Go语言技术书籍也会迎来一波小高峰</strong>。</p>
<p>618购物节前夕，我就来简单评点一下2021年至今出版的口碑还不错的Go语言新书(按出版时间顺序)，大家可以趁打折力度较大的窗口按需从电商平台购买纸版书或电子书渠道购买电子书阅读^_^。</p>
<hr />
<h3>1. <a href="https://book.douban.com/subject/35556889/">《Go语言底层原理剖析》</a> 2021.8</h3>
<p>Go语言是带有GC与运行时的语言，这就意味着很多东西不是“表面”看到的那样，比如string、切片、map等类型在运行时的表示与我们在源码中看到的有很大不同。要想玩转Go语言，不下沉到“原理”这一层还真不行。</p>
<p>《Go语言底层原理剖析》这本书显然也是定位了那些对Go原理有述求的这部分gopher群体。书的作者郑建勋老师是滴滴的高级研发工程师。大家知道，滴滴公司内部使用Go技术栈实现的服务比例是很高的，因此这本书也是郑老师在滴滴“摸爬滚打”后的实践检验的沉淀与总结。</p>
<p>这本书从Go编译构建原理起步，然后过渡到Go的几种常见复合类型(数组、字符串、切片、map)的实现原理的讲解，再到对Go核心语法函数、接口、异常处理的原理说明，最后是Go的精华，也是最难啃的部分：goroutine调度、内存分配与GC。如果从覆盖的内容全面性上，应该说基本都包含到了。</p>
<p>笔者在微信读书上对整本书做了阅读，从阅读体验来看，郑老师的技术十分扎实，讲解也很到位。美中不足的是，有些内容刚刚引发你想继续深入的兴趣时，书籍内容却在这里戛然而止了。如果能继续展开就更好了，也许这是基于书籍篇幅上的考量。</p>
<p>✩豆瓣评分：8.5<br />
✩微信读书推荐值：57.7%</p>
<p>本书在豆瓣口碑与微信读书推荐上存在一些分化，原因这个还不得而知。</p>
<h3>2. <a href="https://book.douban.com/subject/35635836/">《Go语言设计与实现》</a> 2021.11</h3>
<p>《Go语言设计与实现》一书是作者左书祺(Draven)在其同名开源电子书<a href="https://draveness.me/golang/">《Go语言设计与实现》</a>的基础上进一步系统整理和丰富而成。左老师的开源电子书在国内Gopher圈内有着相当好的口碑，他擅长以精美插图的方式对技术细节进行细致入微的讲解，作者甚至还专门出过一篇<a href="https://draveness.me/sketch-and-sketch/">《技术文章配图指南》</a>来说明其文章中插图制作使用的工具以及方法。</p>
<p>和《Go语言底层原理剖析》一样，《Go语言设计与实现》同样聚焦在Go编译器、类型系统与运行机制的原理层面，两本书对原理的说明角度和风格各有特点，就看读者喜欢哪种。更好的方法是主题阅读，两个相互参照的看。</p>
<p>编写面向Go底层原理的书是有一定“风险”的，很容易随着时间的流逝而变得“outdated”，这是因为Go语言还在快速演进中，其底层实现也在不断变化，远没有Java那样成熟，所以很难像神作《深入理解java虚拟机》那般“稳定”，需要不断更新。在这一点上，纸板书反倒没有开源电子书优势明显，后者可做到以快速持续的迭代更新。</p>
<p>不过笔者觉得：要想对一个语言机制的底层原理理解透彻，光是掌握其当前的实现机制还不够，了解其实现机制的历史演进过程将大有裨益，而上面的两本书的价值恰恰还可以体现在这个方面，尤其是当书中的实现机制在将来过时的时候。</p>
<p>✩豆瓣评分：8.5<br />
✩微信读书推荐值：未上架</p>
<h3>3. <a href="https://book.douban.com/subject/35720728/">《Go语言精进之路》</a> 2021.12.17</h3>
<p>写Go语言语法方面的书风险小，Go书籍的寿命都很长，这是因为<a href="https://go.dev/doc/go1compat">Go1兼容性</a>承诺的存在，这也是Go书籍作者的幸运。</p>
<p><a href="https://item.jd.com/13694000.html">《Go语言精进之路》</a>是<a href="https://tonybai.com/2022/01/15/go-programming-from-beginners-to-masters-is-published">笔者的作品</a>，该作品主要面向一个刚刚Go入门后的Go新手，就像副标题描述的那样，聚焦于告诉一个Go入门新手如何能像Go开发团队那样写出符合Go思维和语言惯例的高质量代码。书中也有一部分底层原理的介绍，但这些介绍也都是为了配合主线的讲解。由于是偏思维、方法与技巧方面的讲解，里面的绝大部分知识点，即使是几年后，依然是有效的。这就像出版于2015年的Go语言圣经《The Go Programming language》目前看毫不过时一样。</p>
<p>笔者自己的书不好自作点评，下面是<a href="https://weibo.com/7541535351/LuUSQlY58">近期一位读者在weibo上主动at我的评价</a>：</p>
<p><img src="https://tonybai.com/wp-content/uploads/reviewing-those-new-go-language-books-coming-out-in-2021-2022-2.png" alt="" /></p>
<p>其他评价/评论大家也可以在书籍的豆瓣页面或微信读书页面上自行查看。</p>
<p>✩豆瓣评分：8.9<br />
✩微信读书推荐值：84.1%</p>
<h3>4. <a href="https://book.douban.com/subject/35852237/">《Go语言定制指南》</a> 2022.2.1</h3>
<p>《Go语言定制指南》是国内Go技术专家柴树衫老师既<a href="https://book.douban.com/subject/34442131/">《Go语言高级编程》</a>后的又一力作，这次内容更加聚焦：围绕Go语法分析树学习Go词法分析、语法分析、语义分析以及中间代码生成的原理，并基于Go语法树对Go语言进行二次改造，基于Go语言语法裁剪出一个极小子集——凹语言，并实现其的解释执行。</p>
<p>更具体来说，书中主要讲解的是go/ast和go/types等Go编译器相关包的用法，比如：结合<a href="https://tonybai.com/2022/05/24/an-example-of-implement-dsl-using-antlr-and-go-part1">Go语言的文法、语法</a>与go/ast包输出的语法树的对应关系；使用go/types进行语义检查的方法等。</p>
<p>这也是目前国内第一本以Go编译器前端为中心的Go语言技术书籍，即便放眼全世界，这也是稀有的。如果你对Go编译器的工作原理、对定制Go语言十分感兴趣，那么此书是你的不二之选。</p>
<p>不过编译器和语言开发是门槛较高的领域，不免会出现“曲高和寡”的境遇，这本书注定是本已是小众的Go社区中的小众群体的菜。</p>
<p>✩豆瓣评分：暂无<br />
✩微信读书推荐值：暂无</p>
<h3>5. 引进版新书简评</h3>
<p>在豆瓣图书搜索Go技术书籍，看到下面几本刚刚出版不久(可能尚未上架)以及即将出版的几本引进版的新书，这里顺便说说。</p>
<ul>
<li><a href="https://book.douban.com/subject/35902219/">《Go语言学习指南：惯例模式与编程实践》</a> 2022.4.29</li>
</ul>
<p>这是O&#8217;Reilly出版社于2021年3月出版的《Learning Go: An Idiomatic Approach to Real-World Go Programming》的中译版，中文版我还没有来得及读，不过原版我是粗略读过的。这本书面向Go入门群体，同时结合一些实战的例子，与《The Go Programming Language》的受众群体相似度很高。</p>
<p>这本书(原版)整体质量很高，语言精炼，讲解全面，更重要的是它似乎也是第一个包含Go泛型内容的Go入门书，只不过出版时，Go泛型尚未正式发布。今年3月份<a href="https://tonybai.com/2022/04/20/some-changes-in-go-1-18">Go 1.18泛型落地</a>后，该书作者还对泛型章节做了修订，并在网上提供电子版供读者下载。</p>
<ul>
<li><a href="https://book.douban.com/subject/35909085/">《用Go语言自制解释器》</a> 和<a href="https://book.douban.com/subject/35909089/">《用Go语言自制编译器》</a> 2022.6.1</li>
</ul>
<p>这两本都是索斯藤·鲍尔（Thorsten Ball）在2018年自出版的书！作者使用Go语言手把手教你实现了一门类C语法的Monkey语言，从词法分析、语法分析、建立语法树并进行语法分析，到生成字节码，并实现可以执行该字节码的虚拟机，实现Monkey语言的真实执行。这本书在国外颇受好评。</p>
<p>作者在书中采用的是手写词法分析器和语法分析器的方式，而不是借助<a href="https://tonybai.com/2022/05/10/introduction-of-implement-dsl-using-antlr-and-go">像ANTLR这样的parser生成工具</a>，这可以让读者更加深刻的理解和认知一门编程语言的实现过程，酷感十足。</p>
<h3>6. 小结</h3>
<p>我们看到，2021年来出品的Go技术书籍都获得了不错的口碑，这也说明国内Go语言的整体水准在提升，对于刚刚加入Go社区的小伙伴们，这是真金白银般的好消息，<strong>看好书可以避免走弯路</strong>，节省大量时间与精力！</p>
<p>挑一本适合你的，该出手时就出手吧！</p>
<blockquote>
<p><strong>注意：以上豆瓣评分与微信读书推荐值都是2022.5.31的快照值，不代表后续不会发生变化</strong>。</p>
</blockquote>
<hr />
<p><a href="https://wx.zsxq.com/dweb2/index/group/51284458844544">“Gopher部落”知识星球</a>旨在打造一个精品Go学习和进阶社群！高品质首发Go技术文章，“三天”首发阅读权，每年两期Go语言发展现状分析，每天提前1小时阅读到新鲜的Gopher日报，网课、技术专栏、图书内容前瞻，六小时内必答保证等满足你关于Go语言生态的所有需求！2022年，Gopher部落全面改版，将持续分享Go语言与Go应用领域的知识、技巧与实践，并增加诸多互动形式。欢迎大家加入！</p>
<p><img src="http://image.tonybai.com/img/tonybai/gopher-tribe-zsxq-small-card.png" alt="img{512x368}" /><br />
<img src="http://image.tonybai.com/img/tonybai/go-programming-from-beginner-to-master-qr.png" alt="img{512x368}" /></p>
<p><img src="http://image.tonybai.com/img/tonybai/go-first-course-banner.png" alt="img{512x368}" /><br />
<img src="http://image.tonybai.com/img/tonybai/imooc-go-column-pgo-with-qr.jpg" alt="img{512x368}" /></p>
<p><a href="https://tonybai.com/">我爱发短信</a>：企业级短信平台定制开发专家 https://tonybai.com/。smspush : 可部署在企业内部的定制化短信平台，三网覆盖，不惧大并发接入，可定制扩展； 短信内容你来定，不再受约束, 接口丰富，支持长短信，签名可选。2020年4月8日，中国三大电信运营商联合发布《5G消息白皮书》，51短信平台也会全新升级到“51商用消息平台”，全面支持5G RCS消息。</p>
<p>著名云主机服务厂商DigitalOcean发布最新的主机计划，入门级Droplet配置升级为：1 core CPU、1G内存、25G高速SSD，价格5$/月。有使用DigitalOcean需求的朋友，可以打开这个<a href="https://m.do.co/c/bff6eed92687">链接地址</a>：https://m.do.co/c/bff6eed92687 开启你的DO主机之路。</p>
<p>Gopher Daily(Gopher每日新闻)归档仓库 &#8211; https://github.com/bigwhite/gopherdaily</p>
<p>我的联系方式：</p>
<ul>
<li>微博：https://weibo.com/bigwhite20xx</li>
<li>博客：tonybai.com</li>
<li>github: https://github.com/bigwhite</li>
</ul>
<p><img src="http://image.tonybai.com/img/tonybai/iamtonybai-wechat-qr.png" alt="" /></p>
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。</p>
<p style='text-align:left'>&copy; 2022, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2022/06/01/reviewing-those-new-go-language-books-coming-out-in-2021-2022/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>手把手教你使用ANTLR和Go实现一门DSL语言（第二部分）：文法验证</title>
		<link>https://tonybai.com/2022/05/25/an-example-of-implement-dsl-using-antlr-and-go-part2/</link>
		<comments>https://tonybai.com/2022/05/25/an-example-of-implement-dsl-using-antlr-and-go-part2/#comments</comments>
		<pubDate>Tue, 24 May 2022 22:39:02 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[ANTLR]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[grun]]></category>
		<category><![CDATA[Listener]]></category>
		<category><![CDATA[parser]]></category>
		<category><![CDATA[Trace]]></category>
		<category><![CDATA[Tree]]></category>
		<category><![CDATA[产生式]]></category>
		<category><![CDATA[左递归]]></category>
		<category><![CDATA[归约]]></category>
		<category><![CDATA[文法]]></category>
		<category><![CDATA[树]]></category>
		<category><![CDATA[歧义]]></category>
		<category><![CDATA[编译原理]]></category>
		<category><![CDATA[解析树]]></category>
		<category><![CDATA[词法解析]]></category>
		<category><![CDATA[语法]]></category>
		<category><![CDATA[语法树]]></category>
		<category><![CDATA[语法解析]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=3556</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2022/05/25/an-example-of-implement-dsl-using-antlr-and-go-part2 在本系列的第一篇文章《手把手教你使用ANTLR和Go实现一门DSL语言：设计DSL语法与文法》中，我们已经为气象学家们设计了一门DSL，建立了语法样例，并用ANTLR4文法将DSL定义了出来。按照外部DSL设计与实现的工作流，在这一篇中，我们将对上一篇设计的DSL文法进行验证，看看ANTLR基于我们设计的文法是否能成功生成解析器代码，并且基于生成的解析器是否可以成功处理我们编写的语法样例。 ANTLR文法验证可分为两个阶段，我们分别来看一下。 一. 验证ANTLR是否能解析我们定义的文法(Tdat.g4) 验证ANTLR是否能解析我们的文法Tdat.g4的过程也是通过antlr4尝试生成DSL语法解析器代码的过程，如果顺利生成目标代码，没有报错，则说明我们的Tdat.g4文法至少是符合ANTLR4对文法的要求的。一旦成功，ANTLR就会在特定目录下生成你期望的语法的解析器(parser)代码，比如下面命令将Tdat.g4文法生成目标代码为Go的解析器实现，生成的Go代码位于当前目录下的parser目录下。 $antlr4 -Dlanguage=Go -o parser Tdat.g4 在这个过程中，除了语法错误（比如没用分号结尾，缺少冒号等），我们常会遇到两类错误。 一类是Parser rule的间接左递归问题，比如下面这个例子： // Demo2.g4 grammar Demo2; expr1 : expr2; expr2 : expr1 '+' expr3 &#124; '(' expr2 ')' ; expr3 : INT; INT: DIGIT+; DIGIT: [0-9]; 使用antlr基于上面Demo2.g4生成parser代码，我们会得到下面错误提示： $antlr4 -Dlanguage=Go -o parser Demo2.g4 error(119): Demo2.g4::: The following sets of rules are mutually [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/an-example-of-implement-dsl-using-antlr-and-go-part2-1.png" alt="" /></p>
<p><a href="https://tonybai.com/2022/05/25/an-example-of-implement-dsl-using-antlr-and-go-part2">本文永久链接</a> &#8211; https://tonybai.com/2022/05/25/an-example-of-implement-dsl-using-antlr-and-go-part2</p>
<p>在本系列的第一篇文章<a href="https://tonybai.com/2022/05/24/an-example-of-implement-dsl-using-antlr-and-go-part1">《手把手教你使用ANTLR和Go实现一门DSL语言：设计DSL语法与文法》</a>中，我们已经为气象学家们设计了一门DSL，建立了语法样例，并用ANTLR4文法将DSL定义了出来。按照外部DSL设计与实现的工作流，在这一篇中，我们将对上一篇设计的DSL文法进行验证，看看ANTLR基于我们设计的文法是否能成功生成解析器代码，并且基于生成的解析器是否可以成功处理我们编写的语法样例。</p>
<p>ANTLR文法验证可分为两个阶段，我们分别来看一下。</p>
<h3>一. 验证ANTLR是否能解析我们定义的文法(Tdat.g4)</h3>
<p>验证ANTLR是否能解析我们的文法Tdat.g4的过程也是通过antlr4尝试生成DSL语法解析器代码的过程，如果顺利生成目标代码，没有报错，则说明我们的Tdat.g4文法至少是符合ANTLR4对文法的要求的。一旦成功，ANTLR就会在特定目录下生成你期望的语法的解析器(parser)代码，比如下面命令将Tdat.g4文法生成目标代码为Go的解析器实现，生成的Go代码位于当前目录下的parser目录下。</p>
<pre><code>$antlr4 -Dlanguage=Go -o parser Tdat.g4
</code></pre>
<p>在这个过程中，除了语法错误（比如没用分号结尾，缺少冒号等），我们常会遇到两类错误。</p>
<p>一类是Parser rule的<strong>间接左递归问题</strong>，比如下面这个例子：</p>
<pre><code>// Demo2.g4
grammar Demo2;

expr1 : expr2;
expr2 : expr1 '+' expr3
      | '(' expr2 ')'
      ;
expr3 : INT;

INT: DIGIT+;

DIGIT: [0-9];
</code></pre>
<p>使用antlr基于上面Demo2.g4生成parser代码，我们会得到下面错误提示：</p>
<pre><code>$antlr4 -Dlanguage=Go -o parser Demo2.g4
error(119): Demo2.g4::: The following sets of rules are mutually left-recursive [expr1, expr2]
</code></pre>
<p>以Demo2.g4为例，所谓“间接左递归”，就是expr1产生式中包含expr2，而expr2的产生式规则中又包含了expr1，这种情况Antlr是无法处理的。那么我们需要消除这种“间接左递归”，最直接的方法就是将文法改为“直接左递归”，如下面改后的文法：</p>
<pre><code>grammar Demo2;

expr1 : expr1 '+' expr3
      | '(' expr1 ')'
      ;
expr3 : INT;

INT: DIGIT+;

DIGIT: [0-9];
</code></pre>
<p>这里把expr2这一中间rule去掉了！expr1的产生式中直接包含自己，这种直接左递归是antlr可以支持的。</p>
<p>另一类是词法规则生成式相同导致的归约歧义。这里antlr不会给出error，而会以warning形式提醒开发者。比如下面例子：</p>
<pre><code>grammar Demo1;

prog: expr
    | expr1
    ;

expr: DIGIT AND DIGIT;

expr1: DIGIT MASK DIGIT;

AND : '&amp;' ;

MASK : '&amp;' ;

DIGIT: [0-9];
</code></pre>
<p>antlr处理这个文法时，会提示如下warning：</p>
<pre><code>warning(184): Demo1.g4:14:0: One of the token MASK values unreachable. &amp; is always overlapped by token AND
</code></pre>
<p>意思是MASK这个词法规则总是会被AND这个词法规则所遮蔽，或者说通过这个文法总是能识别到expr，而无法识别并归约到expr1这个规则。这个问题与具体文法设计相关，解决方法可参考<a href="https://github.com/antlr/antlr4/blob/master/doc/lexer-rules.md#lexical-modes">ANTLR提供的词法规则说明</a>。</p>
<p>不过，<strong>即便基于你的文法成功生成Parser代码，也不代表你的文法没有逻辑错误</strong>。我们需要通过一些语法样例来验证生成的Parser是否能正确解析我们的语法样例。</p>
<h3>二. 验证生成的Parser代码是否能正确解析我们的语法样例</h3>
<p>第二阶段的验证有两种方法，最简单的就是使用antlr提供的工具grun。这里我将grun的相关命令打包到一个Makefile中：</p>
<pre><code>// Makefile for debugging Tdat.g4
antlr4_exe = java -jar /usr/local/lib/antlr-4.10.1-complete.jar
grun_exe = java org.antlr.v4.gui.TestRig
target =

all:
    go build

gen:
    $(antlr4_exe) -Dlanguage=Go -o parser Tdat.g4

gen_visitor:
    $(antlr4_exe) -Dlanguage=Go -visitor -o parser Tdat.g4

gen_java:
    $(antlr4_exe) Tdat.g4

gui: gen_java
    javac *.java
    $(grun_exe) Tdat prog $(target) -gui

trace: gen_java
    javac *.java
    $(grun_exe) Tdat prog $(target) -trace

tokens: gen_java
    javac *.java
    $(grun_exe) Tdat prog $(target) -tokens

tree: gen_java
    javac *.java
    $(grun_exe) Tdat prog $(target) -tree

clean:
    rm -fr *.java *.class tdat
</code></pre>
<p>由于grun依赖于基于Tdat.g4生成的java parser代码，所以每个调试命令，如debug、trace、tokens等都需要依赖对应的Java的代码生成。</p>
<p>对于同归属于视觉动物的人类来说，我推荐你先使用图形化选项(gui)对语法样例的解析进行调试，我们以下面的最简单的samples/sample1.t为例：</p>
<pre><code>// samples/sample1.t

r0001: Each { || $speed &gt; 30 } =&gt; ("speed", "temperature", "salinity");
</code></pre>
<p>我们执行下面命令：</p>
<pre><code>$make gui target=samples/sample1.t
java -jar /usr/local/lib/antlr-4.10.1-complete.jar Tdat.g4
javac *.java
java org.antlr.v4.gui.TestRig Tdat prog samples/sample1.t -gui
... ...
</code></pre>
<p>上述命令会打开一个新窗口，显示解析后的语法树：</p>
<p><img src="https://tonybai.com/wp-content/uploads/an-example-of-implement-dsl-using-antlr-and-go-part2-2.png" alt="" /></p>
<p>很幸运！基于Tdat.g4文法生成的Parser可以正确解析sample1.t。</p>
<p>某些时候我们需要查看字符序列被解析为词法元素token的过程，以验证字符序列是否都被正确识别，我们可以通过make tokens来实现：</p>
<pre><code>$make tokens target=samples/sample1.t
java -jar /usr/local/lib/antlr-4.10.1-complete.jar Tdat.g4
javac *.java
java org.antlr.v4.gui.TestRig Tdat prog samples/sample1.t -tokens
[@0,12:16='r0001',&lt;ID&gt;,3:0]
[@1,17:17=':',&lt;':'&gt;,3:5]
[@2,19:22='Each',&lt;'Each'&gt;,3:7]
[@3,24:24='{',&lt;'{'&gt;,3:12]
[@4,26:26='|',&lt;'|'&gt;,3:14]
[@5,27:27='|',&lt;'|'&gt;,3:15]
[@6,29:34='$speed',&lt;METRIC&gt;,3:17]
[@7,36:36='&gt;',&lt;'&gt;'&gt;,3:24]
[@8,38:39='30',&lt;INT&gt;,3:26]
[@9,41:41='}',&lt;'}'&gt;,3:29]
[@10,43:44='=&gt;',&lt;'=&gt;'&gt;,3:31]
[@11,46:46='(',&lt;'('&gt;,3:34]
[@12,47:53='"speed"',&lt;STRING&gt;,3:35]
[@13,54:54=',',&lt;','&gt;,3:42]
[@14,56:68='"temperature"',&lt;STRING&gt;,3:44]
[@15,69:69=',',&lt;','&gt;,3:57]
[@16,71:80='"salinity"',&lt;STRING&gt;,3:59]
[@17,81:81=')',&lt;')'&gt;,3:69]
[@18,82:82=';',&lt;';'&gt;,3:70]
[@19,84:83='&lt;EOF&gt;',&lt;EOF&gt;,4:0]
</code></pre>
<p>通过上述的每一行，我们都可以看到一个token被解析出来，匹配的是哪条词法规则。以第一行为例：”r0001&#8243;被解析出来，成功匹配ID这个token规则。</p>
<p>如果要结合parser规则一并查看匹配规则的步骤，可以用trace命令，通过这个命令的详细输出我们可以对parser规则匹配异常的情况进行诊断：</p>
<pre><code>$make trace target=samples/sample1.t
java -jar /usr/local/lib/antlr-4.10.1-complete.jar Tdat.g4
javac *.java
java org.antlr.v4.gui.TestRig Tdat prog samples/sample1.t -trace
enter   prog, LT(1)=r0001
enter   ruleLine, LT(1)=r0001
enter   ruleID, LT(1)=r0001
consume [@0,12:16='r0001',&lt;33&gt;,3:0] rule ruleID
exit    ruleID, LT(1)=:
consume [@1,17:17=':',&lt;1&gt;,3:5] rule ruleLine
enter   enumerableFunc, LT(1)=Each
consume [@2,19:22='Each',&lt;6&gt;,3:7] rule enumerableFunc
exit    enumerableFunc, LT(1)={
consume [@3,24:24='{',&lt;2&gt;,3:12] rule ruleLine
enter   windowsRange, LT(1)=|
consume [@4,26:26='|',&lt;9&gt;,3:14] rule windowsRange
consume [@5,27:27='|',&lt;9&gt;,3:15] rule windowsRange
exit    windowsRange, LT(1)=$speed
enter   conditionExpr, LT(1)=$speed
enter   primaryExpr, LT(1)=$speed
consume [@6,29:34='$speed',&lt;34&gt;,3:17] rule primaryExpr
exit    primaryExpr, LT(1)=&gt;
enter   comparisonOp, LT(1)=&gt;
consume [@7,36:36='&gt;',&lt;24&gt;,3:24] rule comparisonOp
exit    comparisonOp, LT(1)=30
enter   primaryExpr, LT(1)=30
enter   literal, LT(1)=30
consume [@8,38:39='30',&lt;35&gt;,3:26] rule literal
exit    literal, LT(1)=}
exit    primaryExpr, LT(1)=}
exit    conditionExpr, LT(1)=}
consume [@9,41:41='}',&lt;3&gt;,3:29] rule ruleLine
consume [@10,43:44='=&gt;',&lt;4&gt;,3:31] rule ruleLine
enter   result, LT(1)=(
consume [@11,46:46='(',&lt;11&gt;,3:34] rule result
consume [@12,47:53='"speed"',&lt;37&gt;,3:35] rule result
consume [@13,54:54=',',&lt;10&gt;,3:42] rule result
consume [@14,56:68='"temperature"',&lt;37&gt;,3:44] rule result
consume [@15,69:69=',',&lt;10&gt;,3:57] rule result
consume [@16,71:80='"salinity"',&lt;37&gt;,3:59] rule result
consume [@17,81:81=')',&lt;12&gt;,3:69] rule result
exit    result, LT(1)=;
consume [@18,82:82=';',&lt;5&gt;,3:70] rule ruleLine
exit    ruleLine, LT(1)=&lt;EOF&gt;
exit    prog, LT(1)=&lt;EOF&gt;
</code></pre>
<p>上面这个r0001的逻辑比较简单，我们再来一个复杂的：</p>
<pre><code>//samples/sample2.t

r0002: None { |,30| $temperature &gt; 5 } =&gt; ("speed", "temperature", "salinity");
r0005: Each { |,| (($speed &lt; 5) and (($temperature + 1) &lt; 10)) or ((roundDown($speed) &lt;= 10) and (roundUp($salinity) &gt;= 500))} =&gt; ();
</code></pre>
<p>我们用gui形式输出语法树：</p>
<p><img src="https://tonybai.com/wp-content/uploads/an-example-of-implement-dsl-using-antlr-and-go-part2-3.png" alt="" /></p>
<p>我们看到，虽然sample2.t中的源码逻辑变复杂了，但我们生成的Parser依旧可以成功将其解析为语法树。</p>
<p>如果你觉得grun提供的这些工具的输出都不符合你的胃口，那么好吧，你可以自己动手基于Listener方式自己写你的调试工具，最简单的逻辑：我们遍历一遍Parser生成的语法树，看看语法树每个节点是否符合我们的预期。</p>
<p>Antlr的Go runtime提供了一个<a href="https://github.com/antlr/antlr4/blob/master/runtime/Go/antlr/trace_listener.go">TraceListener</a>的结构，从名字上来看似乎是可以遍历语法树，对Node做trace的。但试用后发现总出panic，不知道是不是用法的问题。</p>
<p>不过自己写一个也不麻烦，我们建立一个trace_listener.go，这个listener将遍历语法树所有节点并按我们期望的格式输出相关信息：</p>
<pre><code>package main

import (
    "fmt"
    "tdat/parser"

    "github.com/antlr/antlr4/runtime/Go/antlr"
)

type TraceListener struct {
    *parser.BaseTdatListener
    p *parser.TdatParser
    t antlr.Tree
}

func NewTraceListener(p *parser.TdatParser, t antlr.Tree) *TraceListener {
    return &amp;TraceListener{
        p: p,
        t: t,
    }
}

func (l *TraceListener) EnterEveryRule(ctx antlr.ParserRuleContext) {
    printLevelPrefix(ctx)
    i := ctx.GetRuleIndex()
    ruleName := l.p.RuleNames[i]
    fmt.Printf("==&gt; %s 《 %s 》\n", ruleName, ctx.GetText())
}

func (l *TraceListener) ExitEveryRule(ctx antlr.ParserRuleContext) {
    printLevelPrefix(ctx)
    i := ctx.GetRuleIndex()
    ruleName := l.p.RuleNames[i]
    fmt.Println("&lt;==", ruleName)
}
</code></pre>
<p>antlr的listener默认对语法树进行<strong>前序遍历</strong>，antlr go runtime中的ParseTreeListener接口包含EnterEveryRule和ExitEveryRule两个方法：</p>
<pre><code>type ParseTreeListener interface {
    VisitTerminal(node TerminalNode)
    VisitErrorNode(node ErrorNode)
    EnterEveryRule(ctx ParserRuleContext)
    ExitEveryRule(ctx ParserRuleContext)
}
</code></pre>
<p>在遍历过程中，这两个方法分别会在进入某节点以及结束遍历某节点时被调用，我们可以在我们的Listener接口实现中重写这两个方法来提取遍历的树的所有节点的信息。</p>
<p>现在我们提供一个main函数来驱动这个调试过程：</p>
<pre><code>func main() {
    println("input file:", os.Args[1])
    input, err := antlr.NewFileStream(os.Args[1])
    if err != nil {
        panic(err)
    }

    lexer := parser.NewTdatLexer(input)
    stream := antlr.NewCommonTokenStream(lexer, 0)
    p := parser.NewTdatParser(stream)
    tree := p.Prog()
    antlr.ParseTreeWalkerDefault.Walk(NewTraceListener(p, tree), tree)
}
</code></pre>
<p>编译运行上面程序：</p>
<pre><code>$make
go build

$./tdat samples/sample1.t 

input file: samples/sample1.t
==&gt; prog 《 r0001:Each{||$speed&gt;30}=&gt;("speed","temperature","salinity"); 》
    ==&gt; ruleLine 《 r0001:Each{||$speed&gt;30}=&gt;("speed","temperature","salinity"); 》
        ==&gt; ruleID 《 r0001 》
        &lt;== ruleID
        ==&gt; enumerableFunc 《 Each 》
        &lt;== enumerableFunc
        ==&gt; windowsRange 《 || 》
        &lt;== windowsRange
        ==&gt; conditionExpr 《 $speed&gt;30 》
            ==&gt; primaryExpr 《 $speed 》
            &lt;== primaryExpr
            ==&gt; comparisonOp 《 &gt; 》
            &lt;== comparisonOp
            ==&gt; primaryExpr 《 30 》
                ==&gt; literal 《 30 》
                &lt;== literal
            &lt;== primaryExpr
        &lt;== conditionExpr
        ==&gt; result 《 ("speed","temperature","salinity") 》
        &lt;== result
    &lt;== ruleLine
&lt;== prog
</code></pre>
<p>这里我用了一种带缩进的格式来查看整个遍历过程以及遍历的每个节点的信息，如果你有你期望输出的格式，可以修改上面的EnterEveryRule和ExitEveryRule方法的实现，总之，怎么方便怎么高效就怎么来！</p>
<p>一些童鞋会问，文法确定了，语法确定了，Parser也可以成功生成，那么还会有解析错误的情况么？这个肯定是有的，笔者在开发的过程中就遇到因词法规则顺序的问题导致语法规则匹配错误的情况，下面就是一个例子：</p>
<pre><code>// Demo.g4
grammar Demo;

prog
    : prule1
    | prule2
    ;

prule1
    : 'repeat' INT
    ;

prule2
    : 'repeat' NONZEROINT
    ;

NONZEROINT
    : [1-9](DIGIT)*
    ;

INT
    : DIGIT+
    ;

fragment
DIGIT
    : [0-9]  // match single digit
    ;

LINE_COMMENT
    : '//' .*? '\r'? '\n' -&gt; skip
    ;

COMMENT
    : '/*' .*? '*/' -&gt; skip
    ;

WS
    : [ \t\r\n]+ -&gt; skip
    ;
</code></pre>
<p>我们用下面语法测试基于上面Demo.g4生成的Parser：</p>
<pre><code>//sample1.t
repeat 15
</code></pre>
<p>我们期望其匹配到的规则为prule1，但实际情况是：</p>
<p><img src="https://tonybai.com/wp-content/uploads/an-example-of-implement-dsl-using-antlr-and-go-part2-4.png" alt="" /></p>
<p>grun -gui的输出结果是Parser匹配到了prule2！这是怎么回事呢？我们用grun -tokens再来看看词法规则匹配的情况：</p>
<pre><code> $grun Demo prog samples/sample1.t -tokens
[@0,1:6='repeat',&lt;'repeat'&gt;,2:0]
[@1,8:9='15',&lt;NONZEROINT&gt;,2:7]
[@2,11:10='&lt;EOF&gt;',&lt;EOF&gt;,3:0]
</code></pre>
<p>我们发现15这个数字匹配到了NONZEROINT这个词法规则，而不是INT。这是因为<strong>ANTLR默认优先匹配排在前面的词法规则</strong>。于是在parser规则层面匹配到prule2就不足为奇了。</p>
<p>这只是一个“故意制造”的例子，即便不用Parser，我们也能“肉眼”识别文法中的问题。但在真实的复杂的语法解析器验证时，这样的未按预期匹配的情况也是很常见的，而且是肉眼难于分辨的，我们需要利用grun提供的工具去仔细诊断。</p>
<h3>三. 小结</h3>
<p>在这一篇中，我们通过ANTLR提供的工具对编写的文法规则进行了验证，并进一步验证了基于该文法生成的Parser是否可以解析我们设计的所有语法样例。</p>
<p>好了，现在我们已经可以成功将语法样例解析并转换为内存中的一颗语法树了？那么有了这棵树后，我们怎么实现需求中的表达式求值、指标计算与输出呢？</p>
<p>在下一篇文章中，我将和大家一起学习如何从语法树中提取我们的语义模型，并对语义模型进行测试验证。</p>
<p>本文中涉及的代码可以在<a href="https://github.com/bigwhite/experiments/tree/master/antlr/tdat">这里</a>下载 &#8211; https://github.com/bigwhite/experiments/tree/master/antlr/tdat 。</p>
<hr />
<p><a href="https://wx.zsxq.com/dweb2/index/group/51284458844544">“Gopher部落”知识星球</a>旨在打造一个精品Go学习和进阶社群！高品质首发Go技术文章，“三天”首发阅读权，每年两期Go语言发展现状分析，每天提前1小时阅读到新鲜的Gopher日报，网课、技术专栏、图书内容前瞻，六小时内必答保证等满足你关于Go语言生态的所有需求！2022年，Gopher部落全面改版，将持续分享Go语言与Go应用领域的知识、技巧与实践，并增加诸多互动形式。欢迎大家加入！</p>
<p><img src="http://image.tonybai.com/img/tonybai/gopher-tribe-zsxq-small-card.png" alt="img{512x368}" /><br />
<img src="http://image.tonybai.com/img/tonybai/go-programming-from-beginner-to-master-qr.png" alt="img{512x368}" /></p>
<p><img src="http://image.tonybai.com/img/tonybai/go-first-course-banner.png" alt="img{512x368}" /><br />
<img src="http://image.tonybai.com/img/tonybai/imooc-go-column-pgo-with-qr.jpg" alt="img{512x368}" /></p>
<p><a href="https://tonybai.com/">我爱发短信</a>：企业级短信平台定制开发专家 https://tonybai.com/。smspush : 可部署在企业内部的定制化短信平台，三网覆盖，不惧大并发接入，可定制扩展； 短信内容你来定，不再受约束, 接口丰富，支持长短信，签名可选。2020年4月8日，中国三大电信运营商联合发布《5G消息白皮书》，51短信平台也会全新升级到“51商用消息平台”，全面支持5G RCS消息。</p>
<p>著名云主机服务厂商DigitalOcean发布最新的主机计划，入门级Droplet配置升级为：1 core CPU、1G内存、25G高速SSD，价格5$/月。有使用DigitalOcean需求的朋友，可以打开这个<a href="https://m.do.co/c/bff6eed92687">链接地址</a>：https://m.do.co/c/bff6eed92687 开启你的DO主机之路。</p>
<p>Gopher Daily(Gopher每日新闻)归档仓库 &#8211; https://github.com/bigwhite/gopherdaily</p>
<p>我的联系方式：</p>
<ul>
<li>微博：https://weibo.com/bigwhite20xx</li>
<li>微信公众号：iamtonybai</li>
<li>博客：tonybai.com</li>
<li>github: https://github.com/bigwhite</li>
<li>“Gopher部落”知识星球：https://public.zsxq.com/groups/51284458844544</li>
</ul>
<p><img src="http://image.tonybai.com/img/tonybai/iamtonybai-wechat-qr.png" alt="" /></p>
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。</p>
<p style='text-align:left'>&copy; 2022, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2022/05/25/an-example-of-implement-dsl-using-antlr-and-go-part2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
