<?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/%e9%93%be%e6%8e%a5%e5%99%a8/feed/" rel="self" type="application/rss+xml" />
	<link>https://tonybai.com</link>
	<description>一个程序员的心路历程</description>
	<lastBuildDate>Wed, 15 Apr 2026 00:30:37 +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 的 16 年：一门为持久而生的编程语言</title>
		<link>https://tonybai.com/2025/11/12/16-years-of-go-a-programming-language-built-to-last/</link>
		<comments>https://tonybai.com/2025/11/12/16-years-of-go-a-programming-language-built-to-last/#comments</comments>
		<pubDate>Wed, 12 Nov 2025 00:25:02 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[AI基础设施]]></category>
		<category><![CDATA[AI服务]]></category>
		<category><![CDATA[API]]></category>
		<category><![CDATA[ArdanLabs]]></category>
		<category><![CDATA[CPU]]></category>
		<category><![CDATA[docker]]></category>
		<category><![CDATA[DWARF5]]></category>
		<category><![CDATA[encoding/json/v2]]></category>
		<category><![CDATA[FlightRecorder]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[go1.0]]></category>
		<category><![CDATA[go1.18]]></category>
		<category><![CDATA[go1.25]]></category>
		<category><![CDATA[godoc]]></category>
		<category><![CDATA[gofmt]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[GOMAXPROCS]]></category>
		<category><![CDATA[gomod]]></category>
		<category><![CDATA[Google]]></category>
		<category><![CDATA[Gopher]]></category>
		<category><![CDATA[goroutine]]></category>
		<category><![CDATA[gotest]]></category>
		<category><![CDATA[goversion]]></category>
		<category><![CDATA[govet]]></category>
		<category><![CDATA[Go语言]]></category>
		<category><![CDATA[GreenTeaGC]]></category>
		<category><![CDATA[ignore指令]]></category>
		<category><![CDATA[KenThompson]]></category>
		<category><![CDATA[Kubernetes]]></category>
		<category><![CDATA[macOS12]]></category>
		<category><![CDATA[prometheus]]></category>
		<category><![CDATA[reddit]]></category>
		<category><![CDATA[RobertGriesemer]]></category>
		<category><![CDATA[RobPike]]></category>
		<category><![CDATA[terraform]]></category>
		<category><![CDATA[testing/synctest]]></category>
		<category><![CDATA[Web服务器]]></category>
		<category><![CDATA[WindowsARM]]></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>
		<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=5380</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2025/11/12/16-years-of-go-a-programming-language-built-to-last 大家好，我是Tony Bai。 每年的十一月，对于全球的 Gopher 而言，都是一个值得纪念的特殊时刻。今年，我们迎来了 Go 语言公开发布的第 16 个年头。 在众多的庆祝文章中，来自 Go 社区的知名组织 Ardan Labs 发布的这篇《Go 的 16 年：一门为持久而生的编程语言》，以其深邃的洞察力和饱满的情感，深深地打动了我们。 这篇文章不仅仅是对 Go 历史里程碑的简单罗列，更是一次对 Go 设计哲学——克制、清晰与长远思考——的深刻致敬。文章精准地捕捉了 Go 从解决 Google 内部的工程困境，到成为现代云原生基石的宏大叙事。我们相信，无论对于已经与 Go 同行多年的资深开发者，还是刚刚踏上 Gopher 之旅的新人，这篇文章都能带来启发与共鸣。 为此，我特将其全文翻译为中文，希望能与中文 Go 社区的各位一同分享这份喜悦与思考。以下是正文： 每年的十一月，Go 社区都会为我们这个时代最具悄然变革力量的编程语言之一，庆祝又一个里程碑。 诞生于 Google 并于 2009 年向世界发布的 Go，旨在解决大规模软件构建、庞大代码库、分布式系统以及跨大洲团队协作的复杂性。十六年后的今天，Go 诞生之初秉持的原则——简洁、快速和可靠——依然指导着它的发展。 正如 Go 团队在去年的周年纪念博文中所写：“Go 是为 2007 年的软件工程问题而构建的，但它仍在解决 2024 年的挑战，以及那些尚未到来的挑战。” 起源故事 [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/2025/16-years-of-go-a-programming-language-built-to-last-1.png" alt="" /></p>
<p><a href="https://tonybai.com/2025/11/12/16-years-of-go-a-programming-language-built-to-last">本文永久链接</a> &#8211; https://tonybai.com/2025/11/12/16-years-of-go-a-programming-language-built-to-last</p>
<p>大家好，我是Tony Bai。</p>
<p>每年的十一月，对于全球的 Gopher 而言，都是一个值得纪念的特殊时刻。今年，我们迎来了 Go 语言公开发布的第 16 个年头。</p>
<p>在众多的庆祝文章中，来自 Go 社区的知名组织 Ardan Labs 发布的这篇《<a href="https://www.ardanlabs.com/news/2025/16-years-of-go-a-programming-language-built-to-last">Go 的 16 年：一门为持久而生的编程语言</a>》，以其深邃的洞察力和饱满的情感，深深地打动了我们。</p>
<p>这篇文章不仅仅是对 Go 历史里程碑的简单罗列，更是一次对 Go 设计哲学——克制、清晰与长远思考——的深刻致敬。文章精准地捕捉了 Go 从解决 Google 内部的工程困境，到成为现代云原生基石的宏大叙事。我们相信，无论对于已经与 Go 同行多年的资深开发者，还是刚刚踏上 Gopher 之旅的新人，这篇文章都能带来启发与共鸣。</p>
<p>为此，我特将其全文翻译为中文，希望能与中文 Go 社区的各位一同分享这份喜悦与思考。以下是正文：</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/paid/the-ultimate-guide-to-go-module-qr.png" alt="" /></p>
<hr />
<p>每年的十一月，Go 社区都会为我们这个时代最具悄然变革力量的编程语言之一，庆祝又一个里程碑。</p>
<p>诞生于 Google 并于 <strong>2009</strong> 年向世界发布的 Go，旨在解决大规模软件构建、庞大代码库、分布式系统以及跨大洲团队协作的复杂性。十六年后的今天，Go 诞生之初秉持的原则——<strong>简洁、快速和可靠</strong>——依然指导着它的发展。</p>
<p>正如 <a href="https://tonybai.com/2024/11/12/go-turns-15">Go 团队在去年的周年纪念博文</a>中所写：“Go 是为 2007 年的软件工程问题而构建的，但它仍在解决 2024 年的挑战，以及那些尚未到来的挑战。”</p>
<h2>起源故事</h2>
<p>这门语言源于 Google 三位工程师——<strong>Robert Griesemer, Rob Pike, 和 Ken Thompson</strong>——的挫败感，他们想要一门像 C 一样快、像 Python 一样高效、并且能满足 Google 基础设施规模化需求的语言。</p>
<p>他们并不想彻底革新编程，他们只是想让编程再次变得令人愉悦。</p>
<p>正如Rob Pike曾经说过的那样，“Go 是一次关于我们能去除什么的实验。”他们去除的过度复杂性、无休止的编译时间和混乱的依赖关系，反而成为了 Go 最大的优势。</p>
<h2>Go 编程语言为何能迅速走红</h2>
<p>Go 不仅仅是又一门新语言；它是对<strong>过度工程化的一次宣言</strong>。其设计目标使其脱颖而出：</p>
<ul>
<li><strong>快速编译</strong>：代码在数秒内完成构建，而非数分钟。</li>
<li><strong>简洁性</strong>：极简的特性集，强调清晰与可读性。</li>
<li><strong>并发</strong>：轻量级的 goroutine，使并发编程变得实用。</li>
<li><strong>静态类型 + 安全性</strong>：在不牺牲开发速度的前提下，保证类型安全。</li>
<li><strong>一流的工具链</strong>：go fmt、go test、go mod 及其他工具，塑造了 Go 的工匠精神文化。</li>
</ul>
<p>这些价值观深深地触动了那些厌倦了语言功能蔓延的工程师们，也触动了那些需要稳定、可维护系统的公司。</p>
<h2>现实世界中的 Go</h2>
<p>多年来，Go 已悄然成为现代Web的支柱。它驱动着 <strong>Docker、Kubernetes、Terraform 和 Prometheus</strong>——当今云原生生态系统的根基。</p>
<p>在 Google 内部，它在后端系统中每秒处理数十亿次请求。在 Google 之外，它已成为初创公司构建分布式系统和企业级工具的首选，这些场景都要求在没有摩擦的情况下获得高性能。</p>
<blockquote>
<p>“Go 诞生于 14 年前，至今它仍是唯一一门让并发感觉如此简单的语言。”</p>
</blockquote>
<p>这种观点体现了 Go 在开发者领域中的独特地位：它既足够古老，经受住了考验，又足够现代，能够不断演进发展。</p>
<h2>值得庆祝的里程碑</h2>
<p>Go 的时间线上，点缀着一些关键时刻，展示了这门语言是如何有意识地演进的：</p>
<ul>
<li><strong>2009年</strong>：Google 正式公开发布 Go语言。</li>
<li><strong>2012年</strong>：Go 1.0 发布，并作出了向后兼容的承诺。</li>
<li><strong>2015–2018年</strong>：Go 成为容器化工具和微服务的标准。</li>
<li><strong>2022年</strong>：泛型在 Go 1.18 中到来——一个期待已久的里程碑。</li>
<li><strong>2024年</strong>：Go 位列全球最常用的十大语言之一，并在 AI 服务和边缘计算领域的采用率迅速增长。</li>
</ul>
<p>正是这种稳定性，加上审慎的创新，让 Go 得以经久不衰。当其他语言追逐潮流时，Go 始终立足于实用性。</p>
<h2>是什么让 Go 与众不同</h2>
<p>与许多在每个新版本中不断膨胀的现代语言不同，Go 的演进一直很保守，而这种克制最终得到了回报。</p>
<p>Go 团队保持了一种罕见的、对向后兼容的承诺。十年前编写的代码，今天依然可以编译和运行。对于那些需要跨越数年甚至数十年维护生产系统的组织来说，这种信任是无价的。</p>
<p>Go 的简洁性也促进了团队协作。开发者可以快速上手代码库并投入工作。没有无休止的语法或模式争论，只有简洁、直接且高效的代码。</p>
<p>这种清晰性塑造了一个重视协作而非“炫技”的社区。</p>
<h2>社区的经验教训</h2>
<p>在一份以前的 <a href="https://www.reddit.com/r/golang/comments/17rx47o/go_was_announced_exactly_14_years_ago_happy/">Reddit 周年纪念帖子</a> 中，开发者们回顾了 Go 是如何改变他们职业生涯的：</p>
<blockquote>
<p>“Go 让我重新爱上了编程。”</p>
<p>“它不花哨，但它能搞定事情，这就是我爱它的地方。”</p>
</blockquote>
<p>这些故事体现了 Go 的不朽精神；与其说是炒作，不如说是把工作做好。</p>
<h2>下一章</h2>
<p>Go 的下一个十年，将不仅仅是关于 Web 服务器和 API。其生态系统正在扩展到<strong>AI 基础设施、数据流</strong>和<strong>边缘计算</strong>等领域，在这些地方，性能、并发和简洁性至关重要。</p>
<p>根据 Go 团队的 15 周年博文，当前的工作重点是：</p>
<ul>
<li>利用现代 CPU 架构，优化运行时性能。</li>
<li>改进生产系统中的遥测、可观测性和性能分析。</li>
<li>确保 Go 能够随着下一代硬件的发展而持续扩展。</li>
</ul>
<p>对于押注 Go 的开发者和组织来说，这意味着一件事：这门语言没有放慢脚步，它正在升级。</p>
<h2>Go的2025年：稳步求精，基础更牢固</h2>
<p>发布于 2025 年 8 月的 Go 1.25 版本，体现了这门语言标志性的演进方式——安静、审慎的改进，而非颠覆。虽然没有破坏性变更，但几项更新有意义地加固了 Go 的基础。通过移除旧的“core type”概念，语言规范得以简化，澄清了类型推断和泛型的工作方式。工具链变得更精简、更快速，工具现在按需构建，go.mod 中加入了新的ignore指令，同时 go vet, go doc, 和 go version 等命令也得到了增强。</p>
<p>在底层，运行时获得了容器感知能力，能够根据 CPU 限制自动调整 GOMAXPROCS，使 Go 在云和边缘环境中更加高效。一个新的实验性垃圾回收器（greenteagc）提供了明显更低的停顿时间，而“ Flight Recorder”追踪则引入了持续的、低开销的可观测性。编译器和链接器现在能生成 DWARF 5 调试信息，以获得更小的二进制文件和更快的构建速度，同时修复了一个微妙的空指针 bug，提升了运行时安全。</p>
<p>在标准库中，开发者现在可以通过 testing/synctest 更容易地测试并发代码，并可以试用更快、更灵活的 encoding/json/v2 包。平台支持也向前迈进——现在要求 macOS 12 或更新版本，而 32 位 Windows ARM 将在此版本后停止支持。</p>
<p>总而言之，Go 1.25 提醒了我们这门语言为何能经久不衰：它在不破坏信任的前提下演进，用稳定、有影响力的进步，取代了喧嚣的炒作。</p>
<p>（来源: <a href="https://go.dev/doc/go1.25?utm_source=chatgpt.com">go.dev/doc/go1.25</a>）</p>
<h2>为 Go 干杯</h2>
<p>在 Go 语言诞生 16 周年之际，我们不妨停下来，细细品味它所代表的意义。它不仅仅是一门编程语言，更是一种工程理念，其核心在于克制、清晰和长远思考。</p>
<p>在 Ardan Labs，我们亲眼见证了 Go 如何帮助团队构建可靠、可扩展的系统，从企业平台到初创原型，无所不包。它帮助工程师专注于真正重要的事情：解决实际问题，而不是与工具较劲。</p>
<p>祝愿 Go 语言再创辉煌一年。</p>
<p><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><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/11/12/16-years-of-go-a-programming-language-built-to-last/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Go 1.25新特性前瞻：GC提速，容器更“懂”Go，json有v2了！</title>
		<link>https://tonybai.com/2025/06/14/go-1-25-foresight/</link>
		<comments>https://tonybai.com/2025/06/14/go-1-25-foresight/#comments</comments>
		<pubDate>Sat, 14 Jun 2025 00:06:39 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[Cgroup]]></category>
		<category><![CDATA[Compiler]]></category>
		<category><![CDATA[container]]></category>
		<category><![CDATA[CoreType]]></category>
		<category><![CDATA[CPU]]></category>
		<category><![CDATA[crypto]]></category>
		<category><![CDATA[DWARF5]]></category>
		<category><![CDATA[encoding]]></category>
		<category><![CDATA[fmt]]></category>
		<category><![CDATA[GC]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[go.mod]]></category>
		<category><![CDATA[Go1]]></category>
		<category><![CDATA[go1.25]]></category>
		<category><![CDATA[gobuild]]></category>
		<category><![CDATA[godoc]]></category>
		<category><![CDATA[GOEXPERIMENT]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[GOMAXPROCS]]></category>
		<category><![CDATA[goroutine]]></category>
		<category><![CDATA[govet]]></category>
		<category><![CDATA[ignore]]></category>
		<category><![CDATA[json]]></category>
		<category><![CDATA[jsonv2]]></category>
		<category><![CDATA[Kubernetes]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[nil]]></category>
		<category><![CDATA[panic]]></category>
		<category><![CDATA[Pointer]]></category>
		<category><![CDATA[runtime]]></category>
		<category><![CDATA[Sprintf]]></category>
		<category><![CDATA[sync]]></category>
		<category><![CDATA[synctest]]></category>
		<category><![CDATA[Testing]]></category>
		<category><![CDATA[TLS]]></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>

		<guid isPermaLink="false">https://tonybai.com/?p=4817</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2025/06/14/go-1-25-foresight 大家好，我是Tony Bai。 每年，Go 语言都会以其严谨而高效的节奏，带来两次版本更新。每一次迭代，Go 团队都在底层、工具链和标准库上持续深耕，为我们开发者提供更稳健、更高效、更安全的开发体验。虽然 Go 1.25 的正式版预计在 2025 年 8 月发布，但随着近期Go 1.25RC1版本的推出，我们基于其非最终版的 Release Notes，已经能一窥其核心亮点了。并且，和之前的版本一样，Go 1.25 带来的许多改进，都如同“无形之手”，你可能无需修改一行代码，甚至无需刻意感知，只需简单升级，便能享受到性能的飞跃、诊断能力的提升以及潜藏错误的暴露。这正是 Go 团队践行其核心原则的极致体现。 今天，就让我们一起“未雨绸缪”，聚焦 Go 1.25 中的核心特性，看看它将如何让 Go 语言变得更加强大。 语言层面：兼容至上，细微进化 Go语言对向后兼容性的承诺，是其最受开发者赞誉的特性之一。Go 1.25 再次延续了这一传统：它没有引入任何影响现有 Go 程序的语言语法变更！ 这意味着你可以放心地升级到 Go 1.25，而无需担忧已有的代码库会因此“崩溃”。 尽管如此，语言规范层面仍有细微的整理和优化，例如移除了“core type”的概念，代之以更详细的描述。这些更多是内部设计文档的完善，对日常 Go 程序的编写并无直接影响，但体现了 Go 语言设计本身的严谨性和持续迭代。兼容性，依然是 Go 坚不可摧的基石。 更详细地说明可以参考我之前的文章《Go 1.25规范大扫除：移除“Core Types”，为更灵活的泛型铺路》。 运行时与编译器：性能与可靠性的“幕后推手” 这一部分是 Go 1.25 带来诸多“无形”强大之处的集中体现，它们直接影响着 Go 程序的运行效率和稳定性。 [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/2025/go-1-25-foresight-1.png" alt="" /></p>
<p><a href="https://tonybai.com/2025/06/14/go-1-25-foresight">本文永久链接</a> &#8211; https://tonybai.com/2025/06/14/go-1-25-foresight</p>
<p>大家好，我是Tony Bai。</p>
<p>每年，Go 语言都会以其严谨而高效的节奏，带来两次版本更新。每一次迭代，Go 团队都在底层、工具链和标准库上持续深耕，为我们开发者提供更稳健、更高效、更安全的开发体验。虽然 Go 1.25 的正式版预计在 2025 年 8 月发布，但随着近期Go 1.25RC1版本的推出，我们基于其非最终版的 Release Notes，已经能一窥其核心亮点了。并且，和之前的版本一样，Go 1.25 带来的许多改进，都如同“无形之手”，你可能无需修改一行代码，甚至无需刻意感知，只需简单升级，便能享受到性能的飞跃、诊断能力的提升以及潜藏错误的暴露。这正是 Go 团队践行其核心原则的极致体现。</p>
<p>今天，就让我们一起“未雨绸缪”，聚焦 Go 1.25 中的核心特性，看看它将如何让 Go 语言变得更加强大。</p>
<h2>语言层面：兼容至上，细微进化</h2>
<p>Go语言对<strong>向后兼容性</strong>的承诺，是其最受开发者赞誉的特性之一。Go 1.25 再次延续了这一传统：<strong>它没有引入任何影响现有 Go 程序的语言语法变更！</strong> 这意味着你可以放心地升级到 Go 1.25，而无需担忧已有的代码库会因此“崩溃”。</p>
<p>尽管如此，语言规范层面仍有细微的整理和优化，例如<a href="https://tonybai.com/2025/03/27/remove-coretypes-from-go-spec">移除了“core type”的概念</a>，代之以更详细的描述。这些更多是内部设计文档的完善，对日常 Go 程序的编写并无直接影响，但体现了 Go 语言设计本身的严谨性和持续迭代。兼容性，依然是 Go 坚不可摧的基石。</p>
<blockquote>
<p>更详细地说明可以参考我之前的文章《<a href="https://tonybai.com/2025/03/27/remove-coretypes-from-go-spec/">Go 1.25规范大扫除：移除“Core Types”，为更灵活的泛型铺路</a>》。</p>
</blockquote>
<h2>运行时与编译器：性能与可靠性的“幕后推手”</h2>
<p>这一部分是 Go 1.25 带来诸多“无形”强大之处的集中体现，它们直接影响着 Go 程序的运行效率和稳定性。</p>
<h3>容器感知型 GOMAXPROCS：更懂容器的 CPU 脾气</h3>
<p>在容器化部署日益普及的今天，Go 程序在 Kubernetes 等环境中运行，常常会遇到一个问题：GOMAXPROCS（控制 Go 运行时使用的最大 CPU 核心数）默认值是宿主机逻辑 CPU 数，而非容器实际被分配的 CPU 限制。这可能导致 CPU 资源浪费，或程序试图抢占过多资源，进而引发调度问题。</p>
<p>Go 1.25 带来了重大改进：在 Linux 系统上，Go 运行时现在会<strong>默认考虑 cgroup 的 CPU 限制（即容器的 CPU limit）</strong> 来设置 GOMAXPROCS 的默认值。如果 CPU limit 低于宿主机核心数，GOMAXPROCS 将自动降到这个更低的限制。此外，Go 运行时还会<strong>定期更新 GOMAXPROCS</strong>，以适应 cgroup 限制的动态变化。这一改进，直接解决了 Go 应用在容器环境中可能存在的资源配置不当问题，使得 Go 程序在 K8s 等云原生环境中运行时更加高效和“智能”，真正做到“物尽其用”。</p>
<blockquote>
<p>更详细地说明可以参考我之前的文章《<a href="https://tonybai.com/2025/04/09/gomaxprocs-defaults-add-cgroup-aware/">Go 1.25新提案：GOMAXPROCS默认值将迎Cgroup感知能力，终结容器性能噩梦？</a>》。</p>
</blockquote>
<h3>新的实验性垃圾收集器：GC开销有望显著降低</h3>
<p>Go 1.25 引入了一个<strong>新的实验性垃圾收集器</strong>，可以通过设置 GOEXPERIMENT=greenteagc 在构建时启用。这个新 GC 的设计旨在改进小对象的标记和扫描性能，并提升 CPU 可扩展性。</p>
<p>根据官方的基准测试，在实际应用中，垃圾回收的开销有望减少 <strong>10% 到 40%</strong>！如果这一实验性优化最终成熟并默认启用，将显著降低 Go 程序的 GC 停顿和整体资源消耗，对于所有 Go 应用（尤其是内存密集型应用）来说，这无疑是巨大的性能红利。</p>
<blockquote>
<p>更详细地说明可以参考我之前的文章《<a href="https://tonybai.com/2025/05/03/go-green-tea-garbage-collector/">Go新垃圾回收器登场：Green Tea GC如何通过内存感知显著降低CPU开销？</a>》。</p>
</blockquote>
<h3>更精准的 Nil Pointer Panic：让隐藏的 Bug 无所遁形</h3>
<p>这是一个虽然可能“打破”一些旧代码，但从长远来看极为重要的改进。Go 1.21 到 1.24 版本之间曾存在一个编译器 bug，导致某些在 os.Open 返回 nil 错误时，仍能“幸运地”继续运行并访问 nil 指针，而没有立即 panic。</p>
<pre><code class="go">// Go 1.21-1.24 曾因编译器bug可能不panic的示例
package main
import "os"
func main() {
    f, err := os.Open("nonExistentFile") // err != nil, f 是 nil
    name := f.Name() // 这里访问了 nil.Name()，但可能不panic
    if err != nil {
        return
    }
    println(name)
}
</code></pre>
<p>在 Go 1.25 中，这个编译器 bug 已经被修复，确保 nil 指针检查会及时且准确地执行。这意味着，上述示例中的代码在 Go 1.25 中将明确引发 nil 指针 panic。</p>
<p>这一变化提高了 Go 程序的运行时可靠性，让那些原本被编译器“侥幸放过”的隐藏 Bug 得以暴露。如果你的代码中存在类似问题，升级后可能需要进行修正，将非 nil 错误检查提前到使用变量之前。</p>
<h3>DWARF版本5 支持：更小更快，调试无忧</h3>
<p>Go 1.25 的编译器和链接器现在默认生成 <strong>DWARFv5 调试信息</strong>。这种更新的调试信息格式，可以有效减少 Go 二进制文件中调试信息所需的空间，并缩短程序的链接时间，对于构建大型 Go 应用程序尤其有利，有助于提升开发效率和 CI/CD 流程的速度。</p>
<blockquote>
<p>更详细地说明可以参考我之前的文章《<a href="https://tonybai.com/2025/05/08/go-dwarf5/">Go 1.25链接器提速、执行文件瘦身：DWARF 5调试信息格式升级终落地</a>》。</p>
</blockquote>
<h2>工具链：武装开发者，提升效率</h2>
<p>Go 语言强大的工具链是其生产力的重要保障。Go 1.25 在此基础上进一步发力，带来多项实用改进。</p>
<ul>
<li><strong>go build -asan 默认内存泄漏检测：Cgo 混合编程更安全</strong></li>
</ul>
<p>对于涉及到 Go 与 C/C++ 代码混合编程的场景，内存泄漏诊断一直是个挑战。Go 1.25 中，go build -asan 选项现在默认在程序退出时进行<strong>内存泄漏检测</strong>，能够报告 C 语言分配但未释放的内存。这大大增强了 Go 混合编程时的内存安全性，有助于发现原生代码中的隐蔽内存问题。</p>
<ul>
<li><strong>go.mod ignore directive：灵活管理超大型仓库</strong></li>
</ul>
<p>go.mod 文件新增了 ignore directive，允许你指定 Go 命令在匹配包模式（如 all 或 ./&#8230;）时应忽略的目录。这些目录下的文件不会被 Go 命令扫描和处理。这对于管理包含大量非 Go 代码、文档、或子模块的超大型代码仓库（Monorepo）非常有用，可以减少构建和扫描时间，提高 Go Modules 的灵活性。</p>
<blockquote>
<p>更详细地说明可以参考我之前的文章《<a href="https://tonybai.com/2025/05/22/go-mod-ignore-directive/">Go工具链进化：go.mod新增ignore指令，破解混合项目构建难题</a>》。</p>
</blockquote>
<ul>
<li><strong>go doc -http：本地文档，即开即用</strong></li>
</ul>
<p>一个看似小巧但能极大提升开发体验的改进。新的 go doc -http 选项，可以启动一个本地文档服务器，显示指定 Go 对象的文档，并自动在浏览器中打开。从此，查阅 Go 文档变得更加便捷、直观。</p>
<blockquote>
<p>更详细地说明可以参考我之前的文章《<a href="https://tonybai.com/2024/09/06/go-doc-add-http-support/">重拾精髓：go doc -http让离线包文档浏览更便捷</a>》。</p>
</blockquote>
<ul>
<li><strong>Vet 工具新分析器：提前发现常见 Bug</strong></li>
</ul>
<p>go vet 工具新增了两个实用的分析器。一个是waitgroup，能报告 sync.WaitGroup.Add 的不正确调用位置（例如在 go 协程内部调用）。另外一个是hostport，能检测并建议修正 fmt.Sprintf(“%s:%d”, host, port) 这种不兼容 IPv6 的地址构造方式，推荐使用 net.JoinHostPort。</p>
<p>这些分析器能帮助开发者在编码阶段就避免常见的并发和网络编程陷阱，进一步提升代码质量和可靠性。</p>
<h2>标准库：功能增强与实验性探索</h2>
<p>标准库的不断演进是 Go 保持活力的重要源泉。Go 1.25 在此也带来了多项关键变化。</p>
<h3>testing/synctest：并发测试的新利器</h3>
<p>Go 1.25 引入了全新的 testing/synctest 包，为并发代码的测试提供了原生支持。它允许你在一个隔离的“气泡”（bubble）中运行测试函数，并且能够控制测试环境中时间（使用伪造时钟）和协程的阻塞/恢复。这极大地方便了并发代码的调试和测试，尤其是那些依赖时间或 Goroutine 调度顺序的复杂场景，提高了测试的可靠性和可控性。</p>
<p>关于该特性，我曾编写过一个“<a href="https://mp.weixin.qq.com/mp/appmsgalbum?__biz=MzIyNzM0MDk0Mg==&amp;action=getalbum&amp;album_id=1509674724631609344#wechat_redirect">征服Go并发测试</a>”的微专栏，欢迎大家扫描订阅，了解关于synctest的设计、实现以及实践方式。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/paid/go-concurrent-test-qr.png" alt="" /></p>
<h3>encoding/json/v2 实验性版本：高性能 JSON 编解码展望</h3>
<p>Go 1.25 引入了一个<strong>新的、实验性的 encoding/json/v2 包</strong>，可以通过设置 GOEXPERIMENT=jsonv2 环境变量在构建时启用。这是对 Go 核心 encoding/json 包的一次重大修订，旨在提升性能和提供更灵活的配置选项。根据初步测试，新实现<strong>在解码性能上显著优于现有版本</strong>，并提供了更多配置 marshaler 和 unmarshaler 的选项。</p>
<p>这是一个令人兴奋的实验性功能，预示着 Go 的 JSON 编解码能力未来将更上一层楼。但作为实验性特性，Go 团队鼓励开发者积极测试自己的程序，并向社区提供反馈，帮助其持续演进。</p>
<blockquote>
<p>关于jsonv2使用的更详细地介绍可以参考我之前的文章《<a href="https://tonybai.com/2025/05/15/go-json-v2/">手把手带你玩转GOEXPERIMENT=jsonv2：Go下一代JSON库初探</a>》。</p>
</blockquote>
<h3>crypto/tls 持续增强：安全与隐私不放松</h3>
<p>Go 在密码学领域的投入从未停止。Go 1.25 中的 crypto/tls 包获得了多项改进：</p>
<ul>
<li>新增 Config.GetEncryptedClientHelloKeys 回调，支持 <strong>Encrypted Client Hello (ECH)</strong> 扩展，进一步提升 TLS 客户端的连接隐私。</li>
<li>默认禁用 TLS 1.2 握手中的 SHA-1 签名算法（但可以通过 tlssha1=1 的 GODEBUG 选项重新启用）。</li>
<li>在<a href="https://tonybai.com/2025/05/21/go-crypto-audit/"> FIPS 140-3 模式</a>下，允许使用更现代的 Ed25519 和 X25519MLKEM768 密钥交换算法。</li>
</ul>
<p>这些改进持续强化了 Go TLS 的安全性、隐私保护和合规性，为迎接未来的量子安全和更严格的安全标准做准备。</p>
<h3>unique 包改进：内存优化再进一步</h3>
<p>unique 包现在能更积极、高效地回收内部化值，有效减少在处理大量重复值时可能出现的内存膨胀问题。这对于 Go 编译器、LSP (Language Server Protocol) 等会大量使用 unique 包的场景，将带来显著的内存和性能优化。</p>
<h3>sync.WaitGroup.Go：并发模式更便捷</h3>
<p>sync.WaitGroup 新增了 Go 方法，为创建和计数 goroutine 提供了一个更便捷的封装，进一步简化了 Go 中常见的并发模式的写法。在之前的文章《<a href="https://tonybai.com/2025/04/03/waitgroup-go-proposal/">WaitGroup.Go要来了？Go官方提案或让你告别Add和Done样板代码</a>》有对这一特性来龙去脉的纤细说明。</p>
<h2>小结</h2>
<p>Go 1.25 的预发布版本，清晰地展现了 Go 语言在性能、可靠性、安全性和开发者体验上的全面提升。这些变化，无论是底层运行时的“无形”优化，还是工具链的智能辅助，都紧密围绕着 Go“生产力”和“生产就绪”的核心原则。</p>
<p>作为 Go 开发者，我们能从中获得的益处是巨大的：你不需要成为系统底层的专家，便能享受到 Go 团队带来的最新技术红利。这种“升级即获益”的模式，正是 Go 语言独特魅力的体现。</p>
<p>Go 语言的旅程永不停歇，它在不断地进化和完善。我鼓励所有 Go 开发者，积极尝试 Go 1.25 RC1 版本，将其应用到你的开发、测试环境中，并向 Go 团队提供宝贵的反馈。你的参与，将是对Go 团队最大的帮助。</p>
<hr />
<p><strong>精进有道，更上层楼</strong></p>
<p><a href="https://mp.weixin.qq.com/s/GWGWTfCRCsOJ_4Pk-pxpHA">极客时间《Go语言进阶课》上架刚好一个月</a>，受到了各位读者的热烈欢迎和反馈。在这里感谢大家的支持。目前我们已经完成了课程模块一『语法强化篇』的 13 讲，为你系统突破 Go 语言的语法认知瓶颈，打下坚实基础。</p>
<p>现在，我们即将进入模块二『设计先行篇』，这不仅包括 API 设计，更涵盖了项目布局、包设计、并发设计、接口设计、错误处理设计等构建高质量 Go 代码的关键要素。</p>
<p>这门进阶课程，是我多年 Go 实战经验和深度思考的结晶，旨在帮助你突破瓶颈，从“会用 Go”迈向“精通 Go”，真正驾驭 Go 语言，编写出更优雅、更高效、更可靠的生产级代码！</p>
<p>扫描下方二维码，立即开启你的 Go 语言进阶之旅！</p>
<p><img src="https://tonybai.com/wp-content/uploads/course-card/iamtonybai-banner-2.gif" alt="" /></p>
<p><strong>感谢阅读！</strong></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/06/14/go-1-25-foresight/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Go 1.23新特性前瞻</title>
		<link>https://tonybai.com/2024/05/30/go-1-23-foresight/</link>
		<comments>https://tonybai.com/2024/05/30/go-1-23-foresight/#comments</comments>
		<pubDate>Wed, 29 May 2024 23:33:38 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[comparble]]></category>
		<category><![CDATA[Compiler]]></category>
		<category><![CDATA[constraints]]></category>
		<category><![CDATA[for]]></category>
		<category><![CDATA[for-range]]></category>
		<category><![CDATA[forloop]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[go1.22]]></category>
		<category><![CDATA[go1.23]]></category>
		<category><![CDATA[go1.24]]></category>
		<category><![CDATA[gobuild]]></category>
		<category><![CDATA[goinstall]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[GoPlayground]]></category>
		<category><![CDATA[gotip]]></category>
		<category><![CDATA[Go语言第一课]]></category>
		<category><![CDATA[Interface]]></category>
		<category><![CDATA[internal]]></category>
		<category><![CDATA[interning]]></category>
		<category><![CDATA[linkname]]></category>
		<category><![CDATA[PGO]]></category>
		<category><![CDATA[range-over-func]]></category>
		<category><![CDATA[Reset]]></category>
		<category><![CDATA[runtime]]></category>
		<category><![CDATA[Telemetry]]></category>
		<category><![CDATA[ticker]]></category>
		<category><![CDATA[Timer]]></category>
		<category><![CDATA[unique]]></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=4183</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2024/05/30/go-1-23-foresight 2024年5月22日，Go 1.23版本功能特性正式冻结，后续将只改bug，不增加新feature。 对Go团队来说，这意味着开始了Go 1.23rc1的冲刺，对我们普通Gopher而言，这意味着是时候对Go 1.23新增的功能做一些前瞻了！ 在Go没有发布Go 1.23rc1之前，我们至少可以通过以下两种渠道体验Go 1.23的最新特性： 通过go install安装tip版本； 使用Go playground在线体验。 注：关于Go tip的安装方法以及Go playground在线体验的详细说明，这里就不赘述了，《Go语言第一课》专栏的“03｜配好环境：选择一种最适合你的Go安装方法”有系统全面的讲解，欢迎订阅阅读。 按照Go Release cycle，Go 1.23将于2024年8月发布！因此目前为时尚早，下面列出的有些变化最终不一定能进入到Go 1.23的最终版本中，有小概率被revert的可能或者推迟到下一个版本(Go 1.24)，所以切记一切变更点要以最终Go 1.23版本发布时为准。 1. 语言变化 Go 1.23语言变化较少，除了range over func试验特性转正，再有就是几个悬而未决的spec变更。 1.1 range over func试验特性转正(61405) Go 1.22版本引入了range over func试验特性，通过GOEXPERIMENT=rangefunc，可以实现函数迭代器。这一特性在Go 1.23版本正式转正，下面代码可以直接使用Go 1.23编译运行： // go1.23-foresight/lang/range-over-function-iterator/main.go package main import "fmt" func Backward[E any](s []E) func(func(int, E) bool) { [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/go-1-23-foresight-1.png" alt="" /></p>
<p><a href="https://tonybai.com/2024/05/30/go-1-23-foresight">本文永久链接</a> &#8211; https://tonybai.com/2024/05/30/go-1-23-foresight</p>
<p>2024年5月22日，<a href="https://github.com/golang/go/milestone/212">Go 1.23版本</a>功能特性正式冻结，后续将只改bug，不增加新feature。</p>
<p><img src="https://tonybai.com/wp-content/uploads/go-1-23-foresight-2.png" alt="" /></p>
<p>对Go团队来说，这意味着开始了Go 1.23rc1的冲刺，对我们普通Gopher而言，这意味着<strong>是时候对Go 1.23新增的功能做一些前瞻了</strong>！</p>
<p>在Go没有发布Go 1.23rc1之前，我们至少可以通过以下两种渠道体验Go 1.23的最新特性：</p>
<ul>
<li>通过go install安装tip版本；</li>
<li>使用<a href="https://go.dev/play/">Go playground</a>在线体验。</li>
</ul>
<blockquote>
<p>注：关于Go tip的安装方法以及Go playground在线体验的详细说明，这里就不赘述了，<a href="http://gk.link/a/10AVZ">《Go语言第一课》</a>专栏的“<a href="https://time.geekbang.org/column/article/427489">03｜配好环境：选择一种最适合你的Go安装方法</a>”有系统全面的讲解，欢迎订阅阅读。</p>
</blockquote>
<p>按照<a href="https://go.dev/wiki/Go-Release-Cycle">Go Release cycle</a>，Go 1.23将于2024年8月发布！因此目前为时尚早，下面列出的有些变化最终不一定能进入到Go 1.23的最终版本中，有小概率被revert的可能或者推迟到下一个版本(Go 1.24)，所以切记一切变更点要以最终Go 1.23版本发布时为准。</p>
<h2>1. 语言变化</h2>
<p>Go 1.23语言变化较少，除了range over func试验特性转正，再有就是几个悬而未决的spec变更。</p>
<h3>1.1 range over func试验特性转正(<a href="https://github.com/golang/go/issues/61405">61405</a>)</h3>
<p><a href="https://tonybai.com/2024/02/18/some-changes-in-go-1-22/">Go 1.22版本</a>引入了<a href="https://go.dev/wiki/RangefuncExperiment">range over func</a>试验特性，通过GOEXPERIMENT=rangefunc，可以实现函数迭代器。这一特性在Go 1.23版本正式转正，下面代码可以直接使用Go 1.23编译运行：</p>
<pre><code>// go1.23-foresight/lang/range-over-function-iterator/main.go

package main

import "fmt"

func Backward[E any](s []E) func(func(int, E) bool) {
    return func(yield func(int, E) bool) {
        for i := len(s) - 1; i &gt;= 0; i-- {
            if !yield(i, s[i]) {
                return
            }
        }
        return
    }
}

func main() {
    sl := []string{"hello", "world", "golang"}
    for i, s := range Backward(sl) {
        fmt.Printf("%d : %s\n", i, s)
    }
}
</code></pre>
<p>使用Go 1.23运行上述示例：</p>
<pre><code>$go run main.go
2 : golang
1 : world
0 : hello
</code></pre>
<p>range over func可以提供一种统一、高效的迭代方式, 为泛型后的自定义容器类提供统一的迭代接口，同时也可以替代部分现有API返回切片的做法, 改为通过迭代器的形式改进性能（通过编译器的优化），甚至还可以为函数式编程风格提供标准迭代机制。</p>
<p>rang over func机制的实现是通过编译器在源码层面的转换，其转换形式大致如下：</p>
<pre><code>// 单循环变量
for x := range f1 {
    ...
}
</code></pre>
<p>将被转换为：</p>
<pre><code>f1(func(x T) bool {
    ...
})
</code></pre>
<p>而另外一种常见的双循环变量形式的for range：</p>
<pre><code>for expr1, expr2 = range f2 {
    ...
}
</code></pre>
<p>将被转换为：</p>
<pre><code>f2(func(#p1 T1, #p2 T2) bool {
    expr1, expr2 = #p1, #p2
    ...
})
</code></pre>
<p>前提是：f1和f2分别要满足标准库中iter包中的下面函数原型形式：</p>
<pre><code>// $GOROOT/src/iter/iter.go
type Seq[V any] func(yield func(V) bool) bool
type Seq2[K, V any] func(yield func(K, V) bool) bool
</code></pre>
<p>此外，for range循环体中如果有break，将被转换为f1/f2中的return false，而如果有continue，则会被转换为return true。当然这只是大致的形式，实际的转换远比这个要复杂很多，要考虑的情况也是十分复杂。更为具体、复杂的转换可以参考<a href="https://github.com/golang/go/blob/master/src/cmd/compile/internal/rangefunc/rewrite.go">Go编译器的实现源码rewrite.go</a></p>
<p>函数迭代器虽然转正，但肯定尚未成熟，后续还会有诸多问题(比如一些corner case)需要解决，比如Russ Cox新开的<a href="https://github.com/golang/go/issues/65236">issue 65236</a>就在讨论是否允许忽略迭代变量；<a href="https://github.com/golang/go/issues/65237">issue 65237</a>将跟踪spec中与range over func相关内容的变更。</p>
<h3>1.2 spec：几个悬而未决的issue</h3>
<ul>
<li><a href="https://github.com/golang/go/issues/66575">明确依赖常量的包级变量初始化时的次序(issue 66575)</a></li>
</ul>
<p>这个issue来自我提出的《<a href="https://tonybai.com/2024/03/29/the-issue-in-pkg-level-var-init-order-in-go-1-22/">Go 1.22引入的包级变量初始化次序问题</a>》，Go 1.23已经修正了该问题，并保持与Go 1.22之前的版本一致，但<a href="https://go.dev/ref/spec">go spec</a>中尚未就此给出明确的说明。</p>
<ul>
<li><a href="https://github.com/golang/go/issues/59104">澄清”严格可比较”(strictly comparable)和”类型约束”(type constraints)等术语 (issue 59104)</a></li>
<li><a href="https://github.com/golang/go/issues/57310">修改关于”类型参数是接口”的说明，避免引起混淆(issue 57310)</a></li>
<li><a href="https://github.com/golang/go/issues/56103">禁止匿名接口类型的循环定义(issue 56103)</a></li>
</ul>
<p>一些issue已经“跳票”多次，不能确定以上issue都能最终在Go 1.23得以解决！</p>
<h2>2. 编译器与运行时</h2>
<h3>2.1 优化了<a href="https://go.dev/doc/pgo">PGO(Profile Guided Optimization)</a>带来的处理开销 (<a href="https://github.com/golang/go/issues/58102">issue 58102</a>)</h3>
<p>Go社区发现启用PGO后，每个cmd/compile调用都会解析完整的PGO pprof配置文件，构建完整的权重图，然后确定与该包相关的内容。这类工作项有很多，并且随着Profile文件的大小和构建包的数量的扩展，构建开销也会增大。尤其是对于那些特别大的项目，PGO Profile很大，这可能会导致构建时间增加100%以上。</p>
<p>Go 1.23对这个问题进行了优化，PGO开销被降到了个位数百分比。</p>
<h3>2.2 限制将来对linkname的使用(<a href="https://github.com/golang/go/issues/67401">67401</a>)</h3>
<p>在Go语言中，//go:linkname指令可以用来链接到标准库或其他包中的未导出符号。比如我们想访问runtime包中的一个未导出函数，例如runtime.nanotime。这个函数返回当前时间的纳秒数。我们可以通过//go:linkname指令链接到这个符号。下面我用一个示例来演示一下这点：</p>
<pre><code>// go1.23-foresight/compiler/golinkname/main.go
package main

import (
    "fmt"
    _ "unsafe" // 必须导入 unsafe 包以使用 //go:linkname
)

// 声明符号链接
//
//go:linkname nanotime runtime.nanotime
func nanotime() int64

func main() {
    // 调用未导出的 runtime.nanotime 函数
    fmt.Println("Current time in nanoseconds:", nanotime())
}
</code></pre>
<p>运行该示例：</p>
<pre><code>$go run main.go
Current time in nanoseconds: 374424337532051
</code></pre>
<p>这种做法一般不推荐，因为它可能导致程序不稳定，并且未来版本的Go可能会改变内部实现（比如nanotime被改名或被删除），破坏你的代码。</p>
<p>Go团队已经意识到这一点，并发现现存开源代码中有很多代码都通过//go:linkname依赖Go项目的internal下的包或Go标准库的未导出符号。这显然不是Go团队想看到的事儿，于是Russ Cox发起issue 67401，旨在考虑限制对//go:linkname的使用。</p>
<p>该issue虽然在Go 1.23 milestone中，但最终是否能落在Go 1.23中还不确定，毕竟这样的调整会导致一些现存代码无法正常编译运行。</p>
<h3>2.3 其他一些优化</h3>
<ul>
<li>优化内存分配器的行为，减少了大内存(带有指针)分配时的长暂停 (<a href="https://github.com/golang/go/issues/31222">issue 31222</a>)</li>
<li>修复Windows下time.Sleep的精度问题(<a href="https://github.com/golang/go/issues/44343">issue 44343</a>)</li>
<li>增加了设置崩溃输出的API runtime/debug.SetCrashOutput(<a href="https://github.com/golang/go/issues/42888">issue 42888</a>)</li>
<li>对内联器继续进行大修：重构优化 (<a href="https://github.com/golang/go/issues/61502">issue 61502</a>)，这是一个长期任务，估计后续版本还会继续。</li>
</ul>
<h2>3. 工具链</h2>
<h3>3.1 新增go telemetry子命令，改进go工具链的遥测能力 (<a href="https://github.com/golang/go/issues/67111">issue 67111</a>)</h3>
<p>Russ Cox去年初就在个人博客上发布了<a href="https://research.swtch.com/telemetry">四篇有关Go Telemetry的文章</a>，在2023 GopherCon大会上，Russ Cox也谈到了<a href="https://tonybai.com/2023/12/10/go-changes/">Go Telemetry对基于数据驱动进行Go演进决策的重要性</a>。Russ Cox亲自创建的<a href="https://github.com/golang/go/issues/58894">“all: add opt-in transparent telemetry to Go toolchain”</a>提案也被Go项目接受。</p>
<p>Go工具链中的telemetry是数据驱动的重要一环，最初<a href="https://github.com/golang/telemetry">golang.org/x/telemetry实验项目</a>被建立。在Go 1.23中，go工具链新增了go telemetry子命令，该子命令就是基于golang.org/x/telemetry实验项目，这也是Go团队实现某一个特性的一贯套路。</p>
<p>go telemetry子命令用法大致如下(以最终版本的doc为准)：</p>
<pre><code>go telemetry - 打印telemetry mode: on, off or local；
go telemetry on - 设置mode为on；即开启telemetry且上传遥测数据。
go telemetry local - 设置mode为local；即telemetry数据仅存储在本地，但不上传。
go telemetry off - 设置mode为off。即关闭telemetry
go clean -telemetry - 清理本地的遥测数据目录。
</code></pre>
<h3>3.2 其他一些改变</h3>
<ul>
<li>go build(-json)支持以json形式输出构建结果(<a href="https://github.com/golang/go/issues/62067">issue 62067</a>)，让构建结果更结构化</li>
<li>移除了对GOROOT_FINAL的支持 (<a href="https://github.com/golang/go/issues/62047">issue 62047</a>)，估计很多人不知道或完全没用过GOROOT_FINAL，我也是如此。</li>
</ul>
<h2>4. 标准库</h2>
<h3>4.1 Timer/Ticker变化</h3>
<p>timer/ticker的stop/reset问题一直困扰Go团队，Go 1.23的两个重要fix期望能从根本上解决这个问题：</p>
<ul>
<li>Timer/Ticker的GC不再需要Stop(<a href="https://github.com/golang/go/issues/61542">issue 61542</a>)</li>
</ul>
<p>程序不再引用的Timer和Ticker将立即有资格进行垃圾回收，即使它们的Stop方法尚未被调用。Go的早期版本直到触发后才会收集未停止的Timer，并且从未收集未停止的Ticker。</p>
<ul>
<li>Timer/Ticker的Stop/Reset后不再接收旧值(<a href="https://github.com/golang/go/issues/37196">issue 37196</a>)</li>
</ul>
<p>与Timer或Ticker关联的计时器channel现在改为无缓冲的了，即容量为0 。此更改的主要效果是Go现在保证任何对Reset或Stop方法的调用，调用之前不会发送或接收任何陈旧值。 Go的早期版本使用带有缓冲区的channel，因此很难正确使用Reset和Stop。此更改的一个明显效果是计时器channel的len和cap现在返回0而不是1，这可能会影响轮询长度以确定是否在计时器channel上接收的程序。通过GODEBUG设置asynctimerchan=1可恢复异步通道行为。</p>
<h3>4.2 新增unique包(<a href="https://github.com/golang/go/issues/62483">issue 62483</a>)</h3>
<p>unique包的灵感来自于第三方包<a href="https://github.com/go4org/intern">go4.org/intern</a>，也是为了弥补Go语言缺乏对<a href="https://en.wikipedia.org/wiki/Interning_(computer_science)">interning</a>内置支持的空缺。</p>
<p>根据wikipedia的描述，interning是按需重复使用具有同等值对象的技术，减少创建新对象的动作。这种创建模式经常用于不同编程语言中的数和字符串，可以避免不必要的对象重复分配的开销。</p>
<p>Go unique包即是Go的interning机制的实现，unique包提供了一种高效的值去重和快速比较的机制，可以用于优化某些特定场景下的程序性能。</p>
<p>unique包提供给开发人员的API接口非常简单：Make用来创建Handle实例，Handle的方法Value用于获取值的拷贝。下面是一个使用unique包的典型示例：</p>
<pre><code>// go1.23-foresight/lib/unique/main.go
package main

import (
    "fmt"
    "unique"
)

func main() {
    // 创建唯一Handle
    s1 := unique.Make("hello")
    s2 := unique.Make("world")
    s3 := unique.Make("hello")

    // s1和s3是相等的，因为它们是同一个字符串值
    fmt.Println(s1 == s3) // true
    fmt.Println(s1 == s2) // false

    // 从Handle获取原始值
    fmt.Println(s1.Value()) // hello
    fmt.Println(s2.Value()) // world
}
</code></pre>
<p>代码和输出结果都不难理解，这类就不赘述了。</p>
<h3>4.3 函数迭代器相关</h3>
<p>前面说过，函数迭代器转正了。标准库中有一些包立即就提供了一些便利的、可以与函数迭代器一起使用的函数，以slices、maps两个后加入Go标准库的泛型容器包为主。</p>
<p>其中slices包增加了：All、Values、Backward、Collect、AppendSeq、Sortted、SortedFunc、SortedStableFunc和Chunk。maps包增加了All、Keys、Values、Insert和Collect。</p>
<p>我们以slices包的All和Backward来构建一个示例，直观感受一下：</p>
<pre><code>// go1.23-foresight/lib/slices/main.go

package main

import (
    "fmt"
    "slices"
)

func main() {
    sl := []string{"hello", "world", "golang"}

    for i, s := range slices.All(sl) {
        fmt.Printf("%d : %s\n", i, s)
    }

    for i, s := range slices.Backward(sl) {
        fmt.Printf("%d : %s\n", i, s)
    }
}
</code></pre>
<p>运行该示例：</p>
<pre><code>$go run main.go
0 : hello
1 : world
2 : golang
2 : golang
1 : world
0 : hello
</code></pre>
<p>和以往一样，Go标准库是变化最多的一块儿，但篇幅有限，这里不便枚举，大家可以自行查看Go 1.23里程碑，选择自己关注的标准库变化，并深入研究。</p>
<h2>5. 小结</h2>
<p>本文主要预览了Go 1.23版本即将带来的新特性和变化。</p>
<p>首先在语言层面，range over func试验特性正式转正，提供统一高效的迭代方式；同时也会修复之前一些悬而未决的规范问题。</p>
<p>其次，在编译器和运行时方面，Go 1.23将优化PGO带来的开销，限制对linkname的使用，优化内存分配器和内联器等。工具链方面，新增telemetry子命令改进遥测能力。</p>
<p>标准库也有不少变化，比如Timer/Ticker的相关修复，新增unique包实现interning机制，以及为函数迭代器新增一些辅助函数。</p>
<p>Go 1.23的Release Notes的编写方式也做了调整，详细内容可参考我的公号文章<a href="https://mp.weixin.qq.com/s/FgNepRmRJlw-7RBnSiywQA">《Go 1.23 Release Notes编写方式改进！》</a>。</p>
<p>总的来说，Go 1.23包含了语法、编译器、运行时、工具链和标准库等多方面的改进，其中最主要集中在编译器性能优化、PGO特性增强、新编译器功能实现以及标准库增强等方面。</p>
<p>不过由于Go 1.23尚未发布，文中部分变化还可能被修改或推迟到下个版本。最终还是以正式发布版为准。文末也列出了一些相关资源链接，方便读者深入了解。</p>
<p>截至发文时，Go 1.23 milestone已经完成59%(https://github.com/golang/go/milestone/212)，还有188个open的issue待解决。究竟Go 1.23最终会做出哪些改变，让我们拭目以待！</p>
<p>最后，感谢Go团队以及所有Go 1.23贡献者做出的伟大工作！</p>
<p>文本涉及的源码可以在<a href="https://github.com/bigwhite/experiments/tree/master/go1.23-foresight">这里</a>下载。</p>
<h2>6. 参考资料</h2>
<ul>
<li><a href="https://github.com/golang/go/milestone/212">Go 1.23版本里程碑</a> &#8211; https://github.com/golang/go/milestone/212</li>
<li><a href="https://tip.golang.org/doc/next">Next Release Notes Draft</a> &#8211; https://tip.golang.org/doc/next</li>
<li><a href="https://dev.golang.org/release">Go Release Dashboard</a> &#8211; https://dev.golang.org/release</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>
</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/05/30/go-1-23-foresight/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Go未用代码消除与可执行文件瘦身</title>
		<link>https://tonybai.com/2024/05/05/dead-code-elimination-and-executable-file-slimming-in-go/</link>
		<comments>https://tonybai.com/2024/05/05/dead-code-elimination-and-executable-file-slimming-in-go/#comments</comments>
		<pubDate>Sat, 04 May 2024 22:31:11 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[dead-code-elimination]]></category>
		<category><![CDATA[dependency]]></category>
		<category><![CDATA[deps]]></category>
		<category><![CDATA[gcflags]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[go.mod]]></category>
		<category><![CDATA[go.sum]]></category>
		<category><![CDATA[gobuild]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[GOROOT]]></category>
		<category><![CDATA[Inline]]></category>
		<category><![CDATA[link]]></category>
		<category><![CDATA[Package]]></category>
		<category><![CDATA[Reflect]]></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=4151</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2024/05/05/dead-code-elimination-and-executable-file-slimming-in-go 在日常编写Go代码时，我们会编写很多包，也会在编写的包中引入了各种依赖包。在大型Go工程中，这些直接依赖和间接依赖的包数目可能会有几十个甚至上百个。依赖包有大有小，但通常我们不会使用到依赖包中的所有导出函数或类型方法。 这时Go初学者就会有一个疑问：这些直接依赖包和间接依赖包中的所有代码是否会进入到最终的可执行文件中呢？即便我们只是使用了某个依赖包中的一个导出函数。 这里先给出结论：不会！在这篇文章中，我们就来探索一下这个话题，了解一下其背后的支撑机制以及对Go可执行文件Size的影响。 1. 实验：哪些函数进入到最终的可执行文件中了？ 我们先来做个实验，验证一下究竟哪些函数进入到最终的可执行文件中了！我们建立demo1，其目录结构和部分代码如下： // dead-code-elimination/demo1 $tree -F . . ├── go.mod ├── main.go └── pkga/ └── pkga.go // main.go package main import ( "fmt" "demo/pkga" ) func main() { result := pkga.Foo() fmt.Println(result) } // pkga/pkga.go package pkga import ( "fmt" ) func Foo() string { return "Hello from [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/dead-code-elimination-and-executable-file-slimming-in-go-1.png" alt="" /></p>
<p><a href="https://tonybai.com/2024/05/05/dead-code-elimination-and-executable-file-slimming-in-go">本文永久链接</a> &#8211; https://tonybai.com/2024/05/05/dead-code-elimination-and-executable-file-slimming-in-go</p>
<p>在日常编写Go代码时，我们会编写很多包，也会在编写的包中引入了各种依赖包。在大型Go工程中，这些直接依赖和间接依赖的包数目可能会有几十个甚至上百个。依赖包有大有小，但通常我们不会使用到依赖包中的所有导出函数或类型方法。</p>
<p>这时Go初学者就会有一个疑问：这些直接依赖包和间接依赖包中的所有代码是否会进入到最终的可执行文件中呢？即便我们只是使用了某个依赖包中的一个导出函数。</p>
<p>这里先给出结论：不会！在这篇文章中，我们就来探索一下这个话题，了解一下其背后的支撑机制以及对Go可执行文件Size的影响。</p>
<h2>1. 实验：哪些函数进入到最终的可执行文件中了？</h2>
<p>我们先来做个实验，验证一下究竟哪些函数进入到最终的可执行文件中了！我们建立demo1，其目录结构和部分代码如下：</p>
<pre><code>// dead-code-elimination/demo1
$tree -F .
.
├── go.mod
├── main.go
└── pkga/
    └── pkga.go

// main.go
package main

import (
    "fmt"

    "demo/pkga"
)

func main() {
    result := pkga.Foo()
    fmt.Println(result)
}

// pkga/pkga.go

package pkga

import (
    "fmt"
)

func Foo() string {
    return "Hello from Foo!"
}

func Bar() {
    fmt.Println("This is Bar.")
}
</code></pre>
<p>这个示例十分简单！main函数中调用了pkga包的导出函数Foo，而pkga包中除了Foo函数，还有Bar函数(但并没有被任何其他函数调用)。现在我们来编译一下这个module，然后查看一下编译出的可执行文件中都包含pkga包的哪些函数！(本文实验中使用的<a href="https://tonybai.com/2024/02/18/some-changes-in-go-1-22/">Go为1.22.0版本</a>)</p>
<pre><code>$go build
$go tool nm demo|grep demo
</code></pre>
<p>在输出的可执行文件中，居然没有查到关于pkga的任何符号信息，这可能是Go的优化在“作祟”。我们关闭掉Go编译器的优化后，再来试试：</p>
<pre><code>$go build -gcflags '-l -N'
$go tool nm demo|grep demo
 108ca80 T demo/pkga.Foo
</code></pre>
<p>关掉<a href="https://tonybai.com/2022/10/17/understand-go-inlining-optimisations-by-example">内联优化</a>后，我们看到pkga.Foo出现在最终的可执行文件demo中，但并未被调用的Bar函数并没有进入可执行文件demo中。</p>
<p>我们再来看一下有间接依赖的例子：</p>
<pre><code>// dead-code-elimination/demo2
$tree .
.
├── go.mod
├── main.go
├── pkga
│   └── pkga.go
└── pkgb
    └── pkgb.go

// pkga/pkga.go
package pkga

import (
    "demo/pkgb"
    "fmt"
)

func Foo() string {
    pkgb.Zoo()
    return "Hello from Foo!"
}

func Bar() {
    fmt.Println("This is Bar.")
}
</code></pre>
<p>在这个示例中，我们在pkga.Foo函数中又调用了一个新包pkgb的Zoo函数，我们来编译一下该新示例并查看一下哪些函数进入到最终的可执行文件中：</p>
<pre><code>$go build -gcflags='-l -N'
$go tool nm demo|grep demo
 1093b40 T demo/pkga.Foo
 1093aa0 T demo/pkgb.Zoo
</code></pre>
<p>我们看到：只有程序执行路径上能够触达（被调用）的函数才会进入到最终的可执行文件中！</p>
<p>在复杂的示例中，我们也可以通过带有-ldflags=&#8217;-dumpdep&#8217;的go build命令来查看这种调用依赖关系(这里以demo2为例)：</p>
<pre><code>$go build -ldflags='-dumpdep' -gcflags='-l -N' &gt; deps.txt 2&gt;&amp;1

$grep demo deps.txt
# demo
main.main -&gt; demo/pkga.Foo
demo/pkga.Foo -&gt; demo/pkgb.Zoo
demo/pkga.Foo -&gt; go:string."Hello from Foo!"
demo/pkgb.Zoo -&gt; math/rand.Int31n
demo/pkgb.Zoo -&gt; demo/pkgb..stmp_0
demo/pkgb..stmp_0 -&gt; go:string."Zoo in pkgb"
</code></pre>
<p>到这里，我们知道了Go通过某种机制保证了只有真正使用到的代码才会最终进入到可执行文件中，即便某些代码（比如pkga.Bar）和那些被真正使用的代码（比如pkga.Foo）在同一个包内。这同时保证了最终可执行文件大小在可控范围内。</p>
<p>接下来，我们就来看看Go的这种机制。</p>
<h2>2. 未用代码消除(dead code elimination)</h2>
<p>我们先来复习一下go build的构建过程，以下是 go build 命令的步骤概述：</p>
<ol>
<li>
<p>读取go.mod和go.sum：如果当前目录包含go.mod文件，go build会读取该文件以确定项目的依赖项。它还会根据go.sum文件中的校验和验证依赖项的完整性。</p>
</li>
<li>
<p>计算包依赖图：go build 分析正在构建的包及其依赖项中的导入语句，以构建依赖图。该图表示包之间的关系，使编译器能够确定包的构建顺序。</p>
</li>
<li>
<p>决定要构建的包：基于构建缓存和依赖图，go build 确定需要构建的包。它检查构建缓存，以查看已编译的包是否是最新的。如果自上次构建以来某个包或其依赖项发生了更改，go build将重新构建这些包。</p>
</li>
<li>
<p>调用编译器（go tool compile）：对于每个需要构建的包，go build调用Go编译器（go tool compile）。编译器将Go源代码转换为特定目标平台的机器码，并生成目标文件（.o 文件）。</p>
</li>
<li>
<p>调用链接器（go tool link）：在编译所有必要的包之后，go build 调用 Go 链接器（go tool link）。链接器将编译器生成的目标文件合并为可执行二进制文件或包归档文件。它解析包之间的符号和引用，执行必要的重定位，并生成最终的输出。</p>
</li>
</ol>
<p>上述的整个构建过程可以由下图表示：</p>
<p><img src="https://tonybai.com/wp-content/uploads/dead-code-elimination-and-executable-file-slimming-in-go-2.png" alt="" /></p>
<p>在构建过程中，go build 命令还执行各种优化，例如未用代码消除和内联，以提高生成二进制文件的性能和降低二进制文件的大小。其中的未用代码消除就是保证Go生成的二进制文件大小可控的重要机制。</p>
<p>未用检测算法的实现位于$GOROOT/src/cmd/link/internal/ld/deadcode.go文件中。该算法通过图遍历的方式进行，具体过程如下：</p>
<ol>
<li>从系统的入口点开始，标记所有可通过重定位到达的符号。重定位是两个符号之间的依赖关系。</li>
<li>通过遍历重定位关系，算法标记所有可以从入口点访问到的符号。例如，在主函数main.main中调用了pkga.Foo函数，那么main.main会有对这个函数的重定位信息。</li>
<li>标记完成后，算法会将所有未被标记的符号标记为不可达的未用。这些未被标记的符号表示不会被入口点或其他可达符号访问到的代码。</li>
</ol>
<p>不过，这里有一个特殊的语法元素要注意，那就是带有方法的类型。类型的方法是否进入到最终的可执行文件中，需要考虑不同情况。在deadcode.go，用于标记可达符号的函数实现将可达类型的方法的调用方式分为三种：</p>
<ol>
<li>直接调用</li>
<li>通过可到达的接口类型调用</li>
<li>通过反射调用：reflect.Value.Method（或 MethodByName）或 reflect.Type.Method（或 MethodByName）</li>
</ol>
<p>第一种情况，可以直接将调用的方法被标记为可到达。第二种情况通过将所有可到达的接口类型分解为方法签名来处理。遇到的每个方法都与接口方法签名进行比较，如果匹配，则将其标记为可到达。这种方法非常保守，但简单且正确。</p>
<p>第三种情况通过寻找编译器标记为REFLECTMETHOD的函数来处理。函数F上的REFLECTMETHOD意味着F使用反射进行方法查找，但编译器无法在静态分析阶段确定方法名。因此所有调用reflect.Value.Method 或reflect.Type.Method的函数都是REFLECTMETHOD。调用reflect.Value.MethodByName或reflect.Type.MethodByName且参数为非常量的函数也是REFLECTMETHOD。如果我们找到了REFLECTMETHOD，就会放弃静态分析，并将所有可到达类型的导出方法标记为可达。</p>
<p>下面是一个来自参考资料中的示例：</p>
<pre><code>// dead-code-elimination/demo3/main.go

type X struct{}
type Y struct{}

func (*X) One()   { fmt.Println("hello 1") }
func (*X) Two()   { fmt.Println("hello 2") }
func (*X) Three() { fmt.Println("hello 3") }
func (*Y) Four()  { fmt.Println("hello 4") }
func (*Y) Five()  { fmt.Println("hello 5") }

func main() {
    var name string
    fmt.Scanf("%s", &amp;name)
    reflect.ValueOf(&amp;X{}).MethodByName(name).Call(nil)
    var y Y
    y.Five()
}
</code></pre>
<p>在这个示例中，类型&#42;X有三个方法，类型&#42;Y有两个方法，在main函数中，我们通过反射调用X实例的方法，通过Y实例直接调用Y的方法，我们看看最终X和Y都有哪些方法进入到最后的可执行文件中了：</p>
<pre><code>$go build -gcflags='-l -N'

$go tool nm ./demo|grep main
 11d59c0 D go:main.inittasks
 10d4500 T main.(*X).One
 10d4640 T main.(*X).Three
 10d45a0 T main.(*X).Two
 10d46e0 T main.(*Y).Five
 10d4780 T main.main
... ...
</code></pre>
<p>我们看到通过直接调用的可达类型Y只有代码中直接调用的方法Five进入到最终可执行文件中，而通过反射调用的X的所有方法都可以在最终可执行文件找到！这与前面提到的第三种情况一致。</p>
<h2>3. 小结</h2>
<p>本文介绍了Go语言中的未用代码消除和可执行文件瘦身机制。通过实验验证，只有在程序执行路径上被调用的函数才会进入最终的可执行文件，未被调用的函数会被消除。</p>
<p>本文解释了Go编译过程，包括包依赖图计算、编译和链接等步骤，并指出未用代码消除是其中的重要优化策略。具体的未用代码消除算法是通过图遍历实现的，标记可达的符号并将未被标记的符号视为未用。文章还提到了对类型方法的处理方式。</p>
<p>通过这种未用代码消除机制，Go语言能够控制最终可执行文件的大小，实现可执行文件瘦身。</p>
<p>本文涉及的源码可以在<a href="https://github.com/bigwhite/experiments/tree/master/dead-code-elimination">这里</a>下载。</p>
<h2>4. 参考资料</h2>
<ul>
<li><a href="https://golab.io/talks/getting-the-most-out-of-dead-code-elimination">Getting the most out of Dead Code elimination</a> &#8211; https://golab.io/talks/getting-the-most-out-of-dead-code-elimination</li>
<li><a href="https://github.com/golang/go/issues/6853">all: binaries too big and growing</a> &#8211; https://github.com/golang/go/issues/6853</li>
<li><a href="https://github.com/aarzilli/whydeadcode">aarzilli/whydeadcode</a> &#8211; https://github.com/aarzilli/whydeadcode</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>
</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/05/05/dead-code-elimination-and-executable-file-slimming-in-go/feed/</wfw:commentRss>
		<slash:comments>0</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>Go程序员拥抱C语言简明指南</title>
		<link>https://tonybai.com/2022/05/16/the-short-guide-of-embracing-c-lang-for-gopher/</link>
		<comments>https://tonybai.com/2022/05/16/the-short-guide-of-embracing-c-lang-for-gopher/#comments</comments>
		<pubDate>Sun, 15 May 2022 23:11:16 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[ANSI-C]]></category>
		<category><![CDATA[archive]]></category>
		<category><![CDATA[break]]></category>
		<category><![CDATA[Build]]></category>
		<category><![CDATA[C]]></category>
		<category><![CDATA[C11]]></category>
		<category><![CDATA[C18]]></category>
		<category><![CDATA[C99]]></category>
		<category><![CDATA[Cgo]]></category>
		<category><![CDATA[Clang]]></category>
		<category><![CDATA[clang-format]]></category>
		<category><![CDATA[CMake]]></category>
		<category><![CDATA[Configure]]></category>
		<category><![CDATA[Cpp]]></category>
		<category><![CDATA[C标准库]]></category>
		<category><![CDATA[C语言]]></category>
		<category><![CDATA[DennisRitchie]]></category>
		<category><![CDATA[error]]></category>
		<category><![CDATA[fallthrough]]></category>
		<category><![CDATA[GC]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[gofmt]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[gomodule]]></category>
		<category><![CDATA[Gopher]]></category>
		<category><![CDATA[Go语言第一课]]></category>
		<category><![CDATA[iso]]></category>
		<category><![CDATA[K&R]]></category>
		<category><![CDATA[KenThompson]]></category>
		<category><![CDATA[LeetCode]]></category>
		<category><![CDATA[libc]]></category>
		<category><![CDATA[Lint]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[loccount]]></category>
		<category><![CDATA[macos]]></category>
		<category><![CDATA[Make]]></category>
		<category><![CDATA[Makefile]]></category>
		<category><![CDATA[RobPike]]></category>
		<category><![CDATA[Rust]]></category>
		<category><![CDATA[soname]]></category>
		<category><![CDATA[switch-case]]></category>
		<category><![CDATA[TIOBE]]></category>
		<category><![CDATA[Unix]]></category>
		<category><![CDATA[utf-8]]></category>
		<category><![CDATA[Windows]]></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=3535</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2022/05/16/the-short-guide-of-embracing-c-lang-for-gopher 本文是为于航老师的极客时间专栏《深入C语言和程序运行原理》写的加餐文章《Tony Bai：Go程序员拥抱C语言简明指南》，这里分享给大家，尤其是那些想学习C语言的Gopher们。 你好，我是Tony Bai。 也许有同学对我比较熟悉，看过我在极客时间上的专栏《Tony Bai ·Go语言第一课》，或者是关注了我的博客。那么，作为一个Gopher，我怎么跑到这个C语言专栏做分享了呢？其实，在学习Go语言并成为一名Go程序员之前，我也曾是一名地地道道的C语言程序员。 大学毕业后，我就开始从事C语言后端服务开发工作，在电信增值领域摸爬滚打了十多年。不信的话，你可以去翻翻我的博客，数一数我发的C语言相关文章是不是比关于Go的还多。一直到近几年，我才将工作中的主力语言从C切换到了Go。不过这并不是C语言的问题，主要原因是我转换赛道了。我目前在智能网联汽车领域从事面向云原生平台的先行研发，而在云原生方面，新生代的Go语言有着更好的生态。 不过作为资深C程序员，C语言已经在我身上打下了深深的烙印。虽然Go是我现在工作中的主力语言，但我仍然会每天阅读一些C开源项目的源码，每周还会写下数百行的C代码。在一些工作场景中，特别是在我参与先行研发一些车端中间件时，C语言有着资源占用小、性能高的优势，这一点是Go目前还无法匹敌的。 正因为我有着C程序员和Go程序员的双重身份，接到这个加餐邀请时，我就想到了一个很适合聊的话题——在 Gopher（泛指Go程序员）与C语言之间“牵线搭桥”。在这门课的评论区里，我看到一些同学说，“正是因为学了Go，所以我想学好C”。如果你也对Go比较熟悉，那么恭喜你，这篇加餐简直是为你量身定制的：一个熟悉Go的程序员在学习C时需要注意的问题，还有可能会遇到的坑，我都替你总结好了。 当然，我知道还有一些对Go了解不多的同学，看到这里也别急着退出去。因为C和Go这两门语言的比较，本身就是一个很有意思的话题。今天的加餐，会涉及这两门语言的异同点，通过对C与Go语言特性的比较，你就能更好地理解“C 语言为什么设计成现在这样”。 一. C语言是现代IT工业的根基 在比较C和Go之前，先说说我推荐Gopher学C的最重要原因吧：用一句话总结，C语言在IT工业中的根基地位，是Go和其他语言目前都无法动摇的。 C语言是由美国贝尔实验室的丹尼斯·里奇（Dennis Ritchie）以Unix发明人肯·汤普森（Ken Thompson）设计的B语言为基础而创建的高级编程语言。诞生于上个世纪（精确来说是1972年）的它，到今年（2022年）已到了“知天命”的半百年纪。 年纪大、设计久远一直是“C语言过时论”兴起的根源，但如果你相信这一论断，那就大错特错了。下面，我来为你分析下个中缘由。 首先，我们说说C语言本身：C语言一直在演进，从未停下过脚步。 虽然C语言之父丹尼斯·里奇不幸于2011年永远地离开了我们，但C语言早已成为ANSI（美国国家标准学会）标准以及ISO/IEC（国际标准化组织和国际电工委员会）标准，因此其演进也早已由标准委员会负责。我们来简单回顾一下C语言标准的演进过程： 1989年，ANSI发布了首个C语言标准，被称为C89，又称ANSI C。次年，ISO和IEC把ANSI C89标准定为C语言的国际标准（ISO/IEC 9899:1990），又称C90，它也是C语言的第一个官方版本； 1999年，ISO和IEC发布了C99标准(ISO/IEC 9899:1999)，它是C语言的第二个官方版本； 2011年，ISO和IEC发布了C11标准(ISO/IEC 9899:2011)，它是C语言的第三个官方版本； 2018年，ISO和IEC发布了C18标准(ISO/IEC 9899:2018)，它是C语言的第四个官方版本。 目前，ISO/IEC标准化委员会正在致力于C2x标准的改进与制定，预计它会在2023年发布。 其次，时至今日，C语言的流行度仍然非常高。 著名编程语言排行榜TIOBE的数据显示，各大编程语言年度平均排名的总位次，C语言多年来高居第一，如下图（图片来自TIOBE）所示： 这说明，无论是在过去还是现在，C语言都是一门被广泛应用的工业级编程语言。 最后，也是最重要的一点是：C语言是现代IT工业的根基，我们说C永远不会退出IT行业舞台也不为过。 如今，无论是普通消费者端的Windows、macOS、Android、苹果iOS，还是服务器端的Linux、Unix等操作系统，亦或是各个工业嵌入式领域的操作系统，其内核实现语言都是C语言。互联网时代所使用的主流Web服务器，比如 Nginx、Apache，以及主流数据库，比如MySQL、Oracle、PostgreSQL等，也都是使用C语言开发的杰作。可以说，现代人类每天都在跟由C语言实现的系统亲密接触，并且已经离不开这些系统了。回到我们程序员的日常，Git、SVN等我们时刻在用的源码版本控制软件也都是由C语言实现的。 可以说，C语言在IT工业中的根基地位，不光Go语言替代不了，C++、Rust等系统编程语言也无法动摇，而且不仅短期如此，长期来看也是如此。 总之，C语言具有紧凑、高效、移植性好、对内存的精细控制等优秀特性，这使得我们在任何时候学习它都不会过时。不过，我在这里推荐Gopher去了解和系统学习C语言，其实还有另一个原因。我们继续往下看。 二. C与Go的相通之处：Gopher拥抱C语言的“先天优势” 众所周知，Go 是在C语言的基础上衍生而来的，二者之间有很多相通之处，因此 Gopher 在学习C语言时是有“先天优势”的。接下来，我们具体看看C和Go的相通之处有哪些。 1. 简单且语法同源 Go语言以简单著称，而作为Go先祖的C语言，入门门槛同样不高：Go有25个关键字，C有32个关键字（C89标准），简洁程度在伯仲之间。C语言曾长期作为高校计算机编程教育的首选编程语言，这与C的简单也不无关系。 和Go不同的是，C语言是一个小内核、大外延的编程语言，其简单主要体现在小内核上了。这个“小内核”包括C基本语法与其标准库，我们可以快速掌握它。但需要注意的是，与Go语言“开箱即用、内容丰富”的标准库不同，C标准库非常小（在C11标准之前甚至连thread库都不包含），所以掌握“小内核”后，在LeetCode平台上刷题是没有任何问题的，但要写出某一领域的工业级生产程序，我们还有很多外延知识技能要学习，比如并发原语、操作系统的系统调用，以及进程间通信等。 C语言的这种简单很容易获得Gopher们的认同感。当年Go语言之父们在设计Go语言时，也是主要借鉴了C语言的语法。当然，这与他们深厚的C语言背景不无关系：肯·汤普森（Ken [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/the-short-guide-of-embracing-c-lang-for-gopher-1.png" alt="" /></p>
<p><a href="https://tonybai.com/2022/05/16/the-short-guide-of-embracing-c-lang-for-gopher">本文永久链接</a> &#8211; https://tonybai.com/2022/05/16/the-short-guide-of-embracing-c-lang-for-gopher</p>
<p>本文是为于航老师的极客时间专栏<a href="http://gk.link/a/11osT">《深入C语言和程序运行原理》</a>写的加餐文章<a href="https://time.geekbang.org/column/article/500145">《Tony Bai：Go程序员拥抱C语言简明指南》</a>，这里分享给大家，尤其是那些想学习C语言的Gopher们。</p>
<hr />
<p>你好，我是Tony Bai。</p>
<p>也许有同学对我比较熟悉，看过我在极客时间上的专栏<a href="http://gk.link/a/10AVZ">《Tony Bai ·Go语言第一课》</a>，或者是关注了<a href="https://tonybai.com">我的博客</a>。那么，作为一个Gopher，我怎么跑到这个C语言专栏做分享了呢？其实，在学习Go语言并成为一名Go程序员之前，我也曾是一名地地道道的C语言程序员。</p>
<p>大学毕业后，我就开始从事C语言后端服务开发工作，在电信增值领域摸爬滚打了十多年。不信的话，你可以去翻翻<a href="https://tonybai.com/tag/c">我的博客</a>，数一数我发的C语言相关文章是不是比关于Go的还多。一直到近几年，我才将工作中的主力语言从C切换到了Go。不过这并不是C语言的问题，主要原因是我转换赛道了。我目前在智能网联汽车领域从事面向云原生平台的先行研发，而在云原生方面，新生代的Go语言有着更好的生态。</p>
<p>不过作为资深C程序员，C语言已经在我身上打下了深深的烙印。虽然Go是我现在工作中的主力语言，但我仍然会每天阅读一些C开源项目的源码，每周还会写下数百行的C代码。在一些工作场景中，特别是在我参与先行研发一些车端中间件时，C语言有着资源占用小、性能高的优势，这一点是Go目前还无法匹敌的。</p>
<p>正因为我有着C程序员和Go程序员的双重身份，接到这个加餐邀请时，我就想到了一个很适合聊的话题——在 Gopher（泛指Go程序员）与C语言之间“牵线搭桥”。在这门课的评论区里，我看到一些同学说，“正是因为学了Go，所以我想学好C”。如果你也对Go比较熟悉，那么恭喜你，这篇加餐简直是为你量身定制的：一个熟悉Go的程序员在学习C时需要注意的问题，还有可能会遇到的坑，我都替你总结好了。</p>
<p><strong>当然，我知道还有一些对Go了解不多的同学，看到这里也别急着退出去。</strong>因为C和Go这两门语言的比较，本身就是一个很有意思的话题。今天的加餐，会涉及这两门语言的异同点，通过对C与Go语言特性的比较，你就能更好地理解“C 语言为什么设计成现在这样”。</p>
<h2>一. C语言是现代IT工业的根基</h2>
<p>在比较C和Go之前，先说说我推荐Gopher学C的最重要原因吧：用一句话总结，<strong>C语言在IT工业中的根基地位，是Go和其他语言目前都无法动摇的</strong>。</p>
<p>C语言是由美国贝尔实验室的丹尼斯·里奇（Dennis Ritchie）以Unix发明人肯·汤普森（Ken Thompson）设计的B语言为基础而创建的高级编程语言。诞生于上个世纪（精确来说是1972年）的它，到今年（2022年）已到了“知天命”的半百年纪。 年纪大、设计久远一直是“C语言过时论”兴起的根源，但如果你相信这一论断，那就大错特错了。下面，我来为你分析下个中缘由。</p>
<p><img src="https://tonybai.com/wp-content/uploads/the-short-guide-of-embracing-c-lang-for-gopher-3.jpeg" alt="" /></p>
<p>首先，我们说说C语言本身：<strong>C语言一直在演进，从未停下过脚步</strong>。</p>
<p>虽然C语言之父丹尼斯·里奇不幸于2011年永远地离开了我们，但C语言早已成为ANSI（美国国家标准学会）标准以及ISO/IEC（国际标准化组织和国际电工委员会）标准，因此其演进也早已由标准委员会负责。我们来简单回顾一下C语言标准的演进过程：</p>
<ul>
<li>1989年，ANSI发布了首个C语言标准，被称为C89，又称ANSI C。次年，ISO和IEC把ANSI C89标准定为C语言的国际标准（ISO/IEC 9899:1990），又称C90，它也是C语言的第一个官方版本；</li>
<li>1999年，ISO和IEC发布了<a href="https://www.iso.org/standard/29237.html">C99标准(ISO/IEC 9899:1999)</a>，它是C语言的第二个官方版本；</li>
<li>2011年，ISO和IEC发布了<a href="https://www.iso.org/standard/57853.html">C11标准(ISO/IEC 9899:2011)</a>，它是C语言的第三个官方版本；</li>
<li>2018年，ISO和IEC发布了<a href="https://www.iso.org/standard/74528.html">C18标准(ISO/IEC 9899:2018)</a>，它是C语言的第四个官方版本。<br />
目前，ISO/IEC标准化委员会正在致力于C2x标准的改进与制定，预计它会在2023年发布。</li>
</ul>
<p>其次，<strong>时至今日，C语言的流行度仍然非常高</strong>。</p>
<p>著名编程语言排行榜TIOBE的数据显示，各大编程语言年度平均排名的总位次，C语言多年来高居第一，如下图（图片来自<a href="https://www.tiobe.com/tiobe-index">TIOBE</a>）所示：</p>
<p><img src="https://tonybai.com/wp-content/uploads/the-short-guide-of-embracing-c-lang-for-gopher-2.png" alt="" /></p>
<p>这说明，无论是在过去还是现在，C语言都是一门被广泛应用的工业级编程语言。</p>
<p>最后，也是最重要的一点是：<strong>C语言是现代IT工业的根基</strong>，我们说C永远不会退出IT行业舞台也不为过。</p>
<p>如今，无论是普通消费者端的Windows、macOS、Android、苹果iOS，还是服务器端的Linux、Unix等操作系统，亦或是各个工业嵌入式领域的操作系统，其内核实现语言都是C语言。互联网时代所使用的主流Web服务器，比如 Nginx、Apache，以及主流数据库，比如MySQL、Oracle、PostgreSQL等，也都是使用C语言开发的杰作。可以说，现代人类每天都在跟由C语言实现的系统亲密接触，并且已经离不开这些系统了。回到我们程序员的日常，Git、SVN等我们时刻在用的源码版本控制软件也都是由C语言实现的。</p>
<p>可以说，C语言在IT工业中的根基地位，不光Go语言替代不了，C++、Rust等系统编程语言也无法动摇，而且不仅短期如此，长期来看也是如此。</p>
<p>总之，C语言具有紧凑、高效、移植性好、对内存的精细控制等优秀特性，这使得我们在任何时候学习它都不会过时。不过，我在这里推荐Gopher去了解和系统学习C语言，其实还有另一个原因。我们继续往下看。</p>
<h2>二. C与Go的相通之处：Gopher拥抱C语言的“先天优势”</h2>
<p>众所周知，Go 是在C语言的基础上衍生而来的，二者之间有很多相通之处，因此 Gopher 在学习C语言时是有“先天优势”的。接下来，我们具体看看C和Go的相通之处有哪些。</p>
<h3>1. 简单且语法同源</h3>
<p>Go语言以简单著称，而作为<strong>Go先祖</strong>的C语言，入门门槛同样不高：Go有25个关键字，C有32个关键字（C89标准），简洁程度在伯仲之间。C语言曾长期作为高校计算机编程教育的首选编程语言，这与C的简单也不无关系。</p>
<p>和Go不同的是，C语言是一个<strong>小内核、大外延</strong>的编程语言，其简单主要体现在小内核上了。这个“小内核”包括C基本语法与其标准库，我们可以快速掌握它。但需要注意的是，与Go语言“开箱即用、内容丰富”的标准库不同，<a href="https://en.wikipedia.org/wiki/C_standard_library">C标准库</a>非常小（在C11标准之前甚至连thread库都不包含），所以掌握“小内核”后，在LeetCode平台上刷题是没有任何问题的，但要写出某一领域的工业级生产程序，我们还有很多外延知识技能要学习，比如并发原语、操作系统的系统调用，以及进程间通信等。</p>
<p>C语言的这种简单很容易获得Gopher们的认同感。当年Go语言之父们在设计Go语言时，也是主要借鉴了C语言的语法。当然，这与他们深厚的C语言背景不无关系：肯·汤普森（Ken Thompson）是Unix之父，与丹尼斯·里奇共同设计了C语言；罗博·派克（Rob Pike）是贝尔实验室的资深研究员，参与了Unix系统的演进、Plan9操作系统的开发，还是UTF-8编码的发明人；罗伯特·格瑞史莫（Robert Griesemer）也是用C语言手写Java虚拟机的大神级人物。</p>
<p>Go的第一版编译器就是由肯·汤普森（Ken Thompson）用C语言实现的。并且，Go语言的早期版本中，C代码的比例还不小。以Go语言发布的第一个版本，<a href="https://github.com/golang/go/releases/tag/go1">Go 1.0版本</a>为例，我们通过<a href="https://gitlab.com/esr/loccount">loccount工具</a>对其进行分析，会得到下面的结果：</p>
<pre><code>$loccount .
all          SLOC=460992  (100.00%) LLOC=193045  in 2746 files
Go           SLOC=256321  (55.60%)  LLOC=109763  in 1983 files
C            SLOC=148001  (32.10%)  LLOC=73458   in 368 files
HTML         SLOC=25080   (5.44%)   LLOC=0       in 57 files
asm          SLOC=10109   (2.19%)   LLOC=0       in 133 files
... ...
</code></pre>
<p>这里我们看到，在1.0版本中，C语言代码行数占据了32.10%的份额，这一份额直至Go 1.5版本实现自举后，才下降为不到1%。</p>
<p>我当初对Go“一见钟情”，其中一个主要原因就是Go与C语言的<strong>语法同源。</strong>相对应地，相信这种同源的语法也会让Gopher们喜欢上C语言。</p>
<h3>2. 静态编译且基础范式相同</h3>
<p>除了语法同源，C语言与Go语言的另一个相同点是，它们都是静态编译型语言。这意味着它们都有如下的语法特性：</p>
<ul>
<li>变量与函数都要先声明后才能使用；</li>
<li>所有分配的内存块都要有对应的类型信息，并且在确定其类型信息后才能操作；</li>
<li>源码需要先编译链接后才能运行。</li>
</ul>
<p>相似的编程逻辑与构建过程，让学习C语言的Gopher可以做到无缝衔接。</p>
<p>除此之外，Go 和C的基础编程范式都是命令式编程（imperative programming），即面向算法过程，由程序员通过编程告诉计算机应采取的动作。然后，计算机按程序指令执行一系列流程，生成特定的结果，就像菜谱指定了厨师做蛋糕时应遵循的一系列步骤一样。</p>
<p>从Go看 C，没有面向对象，没有函数式编程，没有泛型（Go 1.18已加入），满眼都是类型与函数，可以说是相当亲切了。</p>
<h3>3. 错误处理机制如出一辙</h3>
<p>对于后端编程语言来说，错误处理机制十分重要。如果两种语言的错误处理机制不同，那么这两种语言的代码整体语法风格很可能大不相同。</p>
<p>在C语言中，我们通常用一个类型为整型的函数返回值作为错误状态标识，函数调用者基于值比较的方式，对这一代表错误状态的返回值进行检视。通常，当这个返回值为0时，代表函数调用成功；当这个返回值为其他值时，代表函数调用出现错误。函数调用者需根据该返回值所代表的错误状态，来决定后续执行哪条错误处理路径上的代码。</p>
<p>C语言这种简单的<strong>基于错误值比较</strong>的错误处理机制，让每个开发人员必须显式地去关注和处理每个错误。经过显式错误处理的代码会更为健壮，也会让开发人员对这些代码更有信心。另外，这些错误就是普通的值，我们不需要额外的语言机制去处理它们，只需利用已有的语言机制，像处理其他普通类型值那样去处理错误就可以了。这让代码更容易调试，我们也更容易针对每个错误处理的决策分支进行测试覆盖。</p>
<p>C语言错误处理机制的这种简单与显式，跟Go语言的设计哲学十分契合，于是Go语言设计者决定继承这种错误处理机制。因此，当Gopher们来到C语言的世界时，无需对自己的错误处理思维做出很大的改变，就可以很容易地适应C语言的风格。</p>
<h2>三. 知己知彼，来看看C与Go的差异</h2>
<p>虽说 Gopher 学习C语言有“先天优势”，但是不经过脚踏实地的学习与实践就想掌握和精通C语言，也是不可能的。而且，C 和Go还是有很大差异的，Gopher 们只有清楚这些差异，做到“知己知彼”，才能在学习过程中分清轻重，有的放矢。俗话说，“磨刀不误砍柴功”，下面我们就一起看看C与Go有哪些不同。</p>
<h3>1. 设计哲学</h3>
<p>在人类自然语言学界，有一个很著名的假说——“<a href="https://en.wikipedia.org/wiki/Linguistic_relativity">萨丕尔-沃夫假说</a>”。这个假说的内容是这样的：<strong>语言影响或决定人类的思维方式</strong>。对我来说，<strong>编程语言也不仅仅是一门工具，它还影响着程序员的思维方式</strong>。每次开始学习一门新的编程语言时，我都会先了解这门编程语言的设计哲学。</p>
<p>每种编程语言都有自己的设计哲学，即便这门语言的设计者没有将其显式地总结出来，它也真真切切地存在，并影响着这门语言的后续演进，以及这门语言程序员的思维方式。我在<a href="http://gk.link/a/10AVZ">《Tony Bai · Go语言第一课》</a>专栏里，将Go语言的设计哲学总结成了5点，分别是<strong>简单、显式、组合、并发和面向工程</strong>。</p>
<p>那么C语言的设计哲学又是什么呢？从表面上看，简单紧凑、性能至上、极致资源、全面移植，这些都可以作为C的设计哲学，但我倾向于一种更有人文气息的说法：<strong>满足和相信程序员</strong>。</p>
<p>在这样的设计哲学下，一方面，C语言提供了几乎所有可以帮助程序员表达自己意图的语法手段，比如宏、指针与指针运算、位操作、pragma指示符、goto语句，以及跳转能力更为强大的longjmp等；另一方面，C语言对程序员的行为并没有做特别严格的限定与约束，C程序员可以利用语言提供的这些语法手段，进行天马行空的发挥：访问硬件、利用指针访问内存中的任一字节、操控任意字节中的每个位（bit）等。总之，C语言假定程序员知道他们在做什么，并选择相信程序员。</p>
<p>C语言给了程序员足够的自由，可以说，在C语言世界，你几乎可以“为所欲为”。但这种哲学也是有代价的，那就是你可能会犯一些莫名其妙的错误，比如悬挂指针，而这些错误很少或不可能在其他语言中出现。</p>
<p>这里再用一个比喻来更为形象地表达下：从Go世界到C世界，就好比在动物园中饲养已久的动物被放归到野生自然保护区，有了更多自由，但周围也暗藏着很多未曾遇到过的危险。因此，学习C语言的Gopher们要有足够的心理准备。</p>
<h3>2. 内存管理</h3>
<p>接下来我们来看C与Go在内存管理方面的不同。我把这一点放在第二位，是因为这两种语言在内存管理上有很大的差异，而且这一差异会给程序员的日常编码带来巨大影响。</p>
<p>我们知道，Go是带有垃圾回收机制（俗称GC）的静态编程语言。使用Go编程时，内存申请与释放，在栈上还是在堆上分配，以及新内存块的清零等等，这一切都是自动的，且对程序员透明。</p>
<p>但在C语言中，上面说的这些都是程序员的责任。手工内存管理在带来灵活性的同时，也带来了极大的风险，其中最常见的就是内存泄露（memory leak）与悬挂指针（dangling pointer）问题。</p>
<p>内存泄露主要指的是<strong>程序员手工在堆上分配的内存在使用后没有被释放（free），进而导致的堆内存持续增加</strong>。而悬挂指针的意思是<strong>指针指向了非法的内存地址</strong>，未初始化的指针、指针所指对象已经被释放等，都是导致悬挂指针的主要原因。针对悬挂指针进行解引用（dereference）操作将会导致运行时错误，从而导致程序异常退出的严重后果。</p>
<p>Go语言带有GC，而C语言不带GC，这都是由各自语言设计哲学所决定的。GC是不符合C语言的设计哲学的，因为一旦有了GC，程序员就远离了机器，程序员直面机器的需求就无法得到满足了。并且，一旦有了GC，无论是在性能上还是在资源占用上，都不可能做到极致了。</p>
<p>在C中，手工管理内存到底是一种什么感觉呢？作为一名有着十多年C开发经验的资深C程序员，我只能告诉你：<strong>与内存斗，其乐无穷</strong>！这是在带GC的编程语言中无法体会到的。</p>
<h3>3. 语法形式</h3>
<p>虽然C语言是Go的先祖，并且Go也继承了很多C语言的语法元素，但在变量/函数声明、行尾分号、代码块是否用括号括起、标识符作用域，以及控制语句语义等方面，二者仍有较大差异。因此，对Go已经很熟悉的程序员在初学C时，受之前编码习惯的影响，往往会踩一些“坑”。基于此，我总结了Gopher学习C语言时需要特别注意的几点，接下来我们具体看看。</p>
<p><strong>第一，注意声明变量时类型与变量名的顺序</strong></p>
<p>前面说过，Go与C都是静态编译型语言，这就要求我们在使用任何变量之前，需要先声明这个变量。但Go采用的变量声明语法颇似Pascal语言，即<strong>变量名在前，变量类型在后</strong>，这与C语言恰好相反，如下所示：</p>
<pre><code>Go:

var a, b int
var p, q *int

vs.

C：
int a, b;
int *p, *q;
</code></pre>
<p>此外，Go支持短变量声明，并且由于短变量声明更短小，无需显式提供变量类型，Go编译器会根据赋值操作符后面的初始化表达式的结果，自动为变量赋予适当类型。因此，它成为了Gopher们喜爱和重度使用的语法。但短声明在C中却不是合法的语法元素：</p>
<pre><code>int main() {
    a := 5; //  error: expected expression
    printf("a = %d\n", a);
}
</code></pre>
<p>不过，和上面的变量类型与变量名声明的顺序问题一样，C编译器会发现并告知我们这个问题，并不会给程序带来实质性的伤害。</p>
<p><strong>第二，注意函数声明无需关键字前缀</strong></p>
<p>无论是C语言还是Go语言，函数都是基本功能逻辑单元，我们也可以说<strong>C程序就是一组函数的集合</strong>。实际上，我们日常的C代码编写大多集中在实现某个函数上。</p>
<p>和变量一样，函数在两种语言中都需要先声明才能使用。Go语言使用func关键字作为<strong>函数声明的前缀</strong>，并且函数返回值列表放在函数声明的最后。但在C语言中，函数声明无需任何关键字作为前缀，函数只支持单一返回值，并且返回值类型放在函数名的前面，如下所示：</p>
<pre><code>Go：
func Add(a, b int) int {
    return a+b
}

vs.

C：
int Add(int a, int b) {
    return a+b;
}
</code></pre>
<p><strong>第三，记得加上代码行结尾的分号</strong></p>
<p>我们日常编写Go代码时，<strong>极少手写分号</strong>。这是因为，Go设计者当初为了简化代码编写，提高代码可读性，选择了<strong>由编译器在词法分析阶段自动在适当位置插入分号的技术路线</strong>。如果你是一个被Go编译器惯坏了的Gopher，来到C语言的世界后，一定不要忘记代码行尾的分号。比如上面例子中的C语言Add函数实现，在return语句后面记得要手动加上分号。</p>
<p><strong>第四，补上“省略”的括号</strong></p>
<p>同样是出于简化代码、增加可读性的考虑，Go设计者最初就取消掉了条件分支语句（if）、选择分支语句（switch）和循环控制语句（for）中条件表达式外围的小括号：</p>
<pre><code>// Go代码
func f() int {
    return 5
}
func main() {
    a := 1
    if a == 1 { // 无需小括号包裹条件表达式
        fmt.Println(a)
    }

    switch b := f(); b { // 无需小括号包裹条件表达式
    case 4:
        fmt.Println("b = 4")
    case 5:
        fmt.Println("b = 5")
    default:
        fmt.Println("b = n/a")
    }

    for i := 1; i &lt; 10; i++ { // 无需小括号包裹循环语句的循环表达式
        a += i
    }
    fmt.Println(a)
}
</code></pre>
<p>这一点恰恰与C语言“背道而驰”。因此，我们在使用C语言编写代码时，务必要想着补上这些括号：</p>
<pre><code>// C代码
int f() {
        return 5;
}

int main() {
    int a = 1;
    if (a == 1) { // 需用小括号包裹条件表达式
        printf("%d\n", a);
    }

    int b = f();
    switch (b) { // 需用小括号包裹条件表达式
    case 4:
        printf("b = 4\n");
        break;
    case 5:
        printf("b = 5\n");
        break;
    default:
        printf("b = n/a\n");
    }

    int i = 0;
    for (i = 1; i &lt; 10; i++) { // 需用小括号包裹循环语句的循环表达式
        a += i;
    }
    printf("%d\n", a);
}
</code></pre>
<p><strong>第五，留意C与Go导出符号的不同机制</strong></p>
<p>C语言通过头文件来声明对外可见的符号，所以我们不用管符号是不是首字母大写的。但在Go中，只有首字母大写的包级变量、常量、类型、函数、方法才是可导出的，即对外部包可见。反之，首字母小写的则为包私有的，仅在包内使用。Gopher一旦习惯了这样的规则，在切换到C语言时，就会产生“心理后遗症”：遇到在其他头文件中定义的首字母小写的函数时，总以为不能直接使用。</p>
<p><strong>第六，记得在switch case语句中添加break</strong></p>
<p>C 语言与Go语言在选择分支语句的语义方面有所不同：C语言的 case 语句中，如果没有显式加入break语句，那么代码将向下自动掉落执行。而Go在最初设计时就重新规定了switch case的语义，默认不自动掉落（fallthrough），除非开发者显式使用fallthrough关键字。</p>
<p>适应了Go的switch case语句的语义后再回来写C代码，就会存在潜在的“风险”。我们来看一个例子：</p>
<pre><code>// C代码：
int main() {
    int a = 1;
    switch(a) {
        case 1:printf("a = 1\n");
        case 2:printf("a = 2\n");
        case 3:printf("a = 3\n");
        default:printf("a = ?\n");
    }
}
</code></pre>
<p>这段代码是按Go语义编写的switch case，编译运行后得到的结果如下：</p>
<pre><code>a = 1
a = 2
a = 3
a = ?
</code></pre>
<p>这显然不符合我们输出“a = 1”的预期。对于初学C的Gopher而言，这个问题影响还是蛮大的，因为这样编写的代码在C编译器眼中是完全合法的，但所代表的语义却完全不是开发人员想要的。这样的程序一旦流入到生产环境，其缺陷可能会引发生产故障。</p>
<p>一些Clint 工具可以检测出这样的问题，因此对于写C代码的Gopher，我建议在提交代码前使用lint工具对代码做一下检查。</p>
<h3>4. 构建机制</h3>
<p>Go与C都是静态编译型语言，它们的源码需要经过编译器和链接器处理，这个过程称为<strong>构建(build)</strong>，构建后得到的可执行文件才是最终交付给用户的成果物。</p>
<p>和Go语言略有不同的是，C语言的构建还有一个预处理（pre-processing）阶段，预处理环节的输出才是C编译器的真正输入。C语言中的宏就是在预处理阶段展开的。不过，Go没有预处理阶段。</p>
<p>C语言的编译单元是一个C源文件（.c），每个编译单元在编译过程中会对应生成一个目标文件（.o/.obj），最后链接器将这些目标文件链接在一起，形成可执行文件。</p>
<p>而Go则是以一个包（package）为编译单元的，每个包内的源文件生成一个.o文件，一个包的所有.o文件聚合（archive）成一个.a文件，链接器将这些目标文件链接在一起形成可执行文件。</p>
<p>Go语言提供了统一的Go命令行工具链，且Go编译器原生支持增量构建，源码构建过程不需要Gopher手工做什么配置。但在C语言的世界中，用于构建C程序的工具有很多，主流的包括gcc/clang，以及微软平台的C编译器。这些编译器原生不支持增量构建，为了提升工程级构建的效率，避免每次都进行全量构建，我们通常会使用第三方的构建管理工具，比如make（Makefile）或CMake。考虑移植性时，我们还会使用到configure文件，用于在目标机器上收集和设置编译器所需的环境信息。</p>
<h3>5. 依赖管理</h3>
<p>我在前面提过，C语言仅提供了一个“小内核”。像依赖管理这类的事情，C语言本身并没有提供跟Go中的Go Module类似的，统一且相对完善的解决方案。在C语言的世界中，我们依然要靠外部工具（比如CMake）来管理第三方的依赖。</p>
<p>C语言的第三方依赖通常以静态库（.a）或动态共享库（.so）的形式存在。如果你的应用要使用静态链接，那就必须在系统中为C编译器提供第三方依赖的静态库文件。但在实际工作中，完全采用静态链接有时是会遇到麻烦的。这是因为，很多操作系统在默认安装时是不带开发包的，也就是说，像 libc、libpthread 这样的系统库只提供了动态共享库版本（如/lib下提供了libc的共享库libc.so.6），其静态库版本是需要自行下载、编译和安装的（如libc的静态库libc.a在安装后是放在/usr/lib下面的)。所以<strong>多数情况下，我们是将****静态、动态****两种链接方式混合在一起使用的</strong>，比如像libc这样的系统库多采用动态链接。</p>
<p>动态共享库通常是有版本的，并且按照一定规则安装到系统中。举个例子，一个名为libfoo的动态共享库，在安装的目录下文件集合通常是这样：</p>
<pre><code>2022-03-10 12:28 libfoo.so -&gt; libfoo.so.0.0.0*
2022-03-10 12:28 libfoo.so.0 -&gt; libfoo.so.0.0.0*
2022-03-10 12:28 libfoo.so.0.0.0*
</code></pre>
<p>按惯例，每个动态共享库都有多个名字属性，包括real name、soname和linker name。下面我们来分别看下。</p>
<ul>
<li>real name：实际包含共享库代码的那个文件的名字(如上面例子中的libfoo.so.0.0.0)。动态共享库的真实版本信息就在real name中，显然real name中的版本号符合<a href="https://semver.org/">语义版本规范</a>，即major.minor.patch。当两个版本的major号一致，说明是向后兼容的两个版本；</li>
<li>soname：shared object name的缩写，也是这三个名字中最重要的一个。无论是在编译阶段还是在运行阶段，系统链接器都是通过动态共享库的soname（如上面例子中的libfoo.so.0）来唯一识别共享库的。我们看到的soname实际上是仅包含major号的共享库名字；</li>
<li>linker name：编译阶段提供给编译器的名字（如上面例子中的libfoo.so）。如果你构建的共享库的real name跟上面例子中libfoo.so.0.0.0类似，带有版本号，那么你在编译器命令中直接使用-L path -lfoo是无法让链接器找到对应的共享库文件的，除非你为libfoo.so.0.0.0提供了一个linker name（如libfoo.so，一个指向libfoo.so.0.0.0的符号链接）。linker name一般在共享库安装时手工创建。<br />
动态共享库有了这三个名称属性，依赖管理就有了依据。但由于在链接的时候使用的是linker name，而linker name并不带有版本号，真实版本与主机环境有关，因此要实现C应用的可重现构建还是比较难。在实践中，我们通常会使用专门的构建主机，项目组将该主机上的依赖管理起来，进而保证每次构建所使用的依赖版本是可控的。同时，应用部署的目标主机上的依赖版本也应该得到管理，避免运行时出现动态共享库版本不匹配的问题。</li>
</ul>
<h3>6. 代码风格</h3>
<p>Go语言是历史上首次实现了代码风格全社区统一的编程语言。它基本上消除了开发人员在代码风格上的无休止的、始终无法达成一致的争论，以及不同代码风格带来的阅读、维护他人代码时的低效。gofmt工具格式化出来的代码风格已经成为Go开发者的一种共识，融入到Go语言的开发文化当中了。所以，如果你让某个Go开发者说说gofmt后的代码风格是什么样的，多数Go开发者可能说不出，因为代码会被gofmt自动变成那种风格，大家已经不再关心风格了。</p>
<p>而在C语言的世界，代码风格仍存争议。但经过多年的演进，以及像Go这样新兴语言的不断“教育”，C社区也在尝试进行这方面的改进，涌现出了像<a href="https://clang.llvm.org/docs/ClangFormat.html">clang-format</a>这样的工具。目前，虽然还没有在全社区达成一致的代码风格（由于历史原因，这很难做到），但已经可以减少很多不必要的争论。</p>
<p>对于正在学习C语言，并进行C编码实践的Gopher，我的建议是：<strong>不要拘泥于使用什么代码风格，先用clang-format，并确定一套风格模板就好</strong>。</p>
<h2>四. 小结</h2>
<p>作为一名对Go跟随和研究了近十年的程序员，我深刻体会到，Go的简单性、性能和生产力使它成为了创建面向用户的应用程序和服务的理想语言。快速的迭代让团队能够快速地作出反应，以满足用户不断变化的需求，让团队可以将更多精力集中在保持灵活性上。</p>
<p>但Go也有缺点，比如缺少对内存以及一些低级操作的精确控制，而C语言恰好可以弥补这个缺陷。C 语言提供的更精细的控制允许更多的精确性，使得C成为低级操作的理想语言。这些低级操作不太可能发生变化，并且C相比Go还提高了性能。所以，如果你是一个有性能与低级操作需求的 Gopher ，就有充分的理由来学习C语言。</p>
<p>C 的优势体现在最接近底层机器的地方，而Go的优势在离用户较近的地方能得到最大发挥。当然，这并不是说两者都不能在对方的空间里工作，但这样做会增加“摩擦”。当你的需求从追求灵活性转变为注重效率时，用C重写库或服务的理由就更充分了。</p>
<p>总之，虽然Go和C的设计有很大的不同，但它们也有很多相似性，具备发挥兼容优势的基础。并且，当我们同时使用这二者时，就可以既有很大的灵活性，又有很好的性能，可以说是相得益彰！</p>
<h2>五. 写在最后</h2>
<p>今天的加餐中，我主要是基于C与Go的比较来讲解的，对于Go语言的特性并没有作详细展开。如果你还想进一步了解Go语言的设计哲学、语法特性、程序设计相关知识，欢迎来学习我在极客时间上的专栏<a href="http://gk.link/a/10AVZ">《Tony Bai ·Go语言第一课》</a>。在这门课里，我会用我十年Gopher的经验，带给你一条系统、完整的Go语言入门路径。</p>
<p>感谢你看到这里，如果今天的内容让你有所收获，欢迎把它分享给你的朋友。</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/16/the-short-guide-of-embracing-c-lang-for-gopher/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Go 1.16中值得关注的几个变化</title>
		<link>https://tonybai.com/2021/02/25/some-changes-in-go-1-16/</link>
		<comments>https://tonybai.com/2021/02/25/some-changes-in-go-1-16/#comments</comments>
		<pubDate>Thu, 25 Feb 2021 02:49:56 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[.NET]]></category>
		<category><![CDATA[Apple]]></category>
		<category><![CDATA[bitbucket]]></category>
		<category><![CDATA[Compiler]]></category>
		<category><![CDATA[embed]]></category>
		<category><![CDATA[generics]]></category>
		<category><![CDATA[Git]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[go-get]]></category>
		<category><![CDATA[go.mod]]></category>
		<category><![CDATA[go.sum]]></category>
		<category><![CDATA[Go1]]></category>
		<category><![CDATA[go1.13]]></category>
		<category><![CDATA[go1.16]]></category>
		<category><![CDATA[go1兼容性]]></category>
		<category><![CDATA[GODEBUG]]></category>
		<category><![CDATA[godoc]]></category>
		<category><![CDATA[goinstall]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[gomodule]]></category>
		<category><![CDATA[GOPATH]]></category>
		<category><![CDATA[Gopher]]></category>
		<category><![CDATA[GOVCS]]></category>
		<category><![CDATA[Go泛型]]></category>
		<category><![CDATA[hg]]></category>
		<category><![CDATA[inittrace]]></category>
		<category><![CDATA[Interface]]></category>
		<category><![CDATA[io/fs]]></category>
		<category><![CDATA[iOS]]></category>
		<category><![CDATA[linker]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[M1]]></category>
		<category><![CDATA[macos]]></category>
		<category><![CDATA[MADV_DONTNEED]]></category>
		<category><![CDATA[retract]]></category>
		<category><![CDATA[RobertGriesemer]]></category>
		<category><![CDATA[runtime]]></category>
		<category><![CDATA[RussCox]]></category>
		<category><![CDATA[Subversion]]></category>
		<category><![CDATA[svn]]></category>
		<category><![CDATA[vfs]]></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=3109</guid>
		<description><![CDATA[辛丑牛年初七开工大吉的日子(2021.2.18)，Go核心开发团队为中国Gopher们献上了大礼 &#8211; Go 1.16版本正式发布了！国内Gopher可以在Go中国官网上下载到Go 1.16在各个平台的安装包： 2020年双12，Go 1.16进入freeze状态，即不再接受新feature，仅fix bug、编写文档和接受安全更新等，那时我曾写过一篇名为《Go 1.16新功能特性不完全前瞻》的文章。当时Go 1.16的发布说明尚处于早期草稿阶段，要了解Go 1.16功能特性都有哪些变化，只能结合当时的release note以及从Go 1.16里程碑中的issue列表中挖掘。 如今Go 1.16版本正式发布了，和当时相比，Go 1.16又有哪些变化呢？在这篇文章中，我们就来一起详细分析一下Go 1.16中那些值得关注的重要变化！ 一. 语言规范 如果你是Go语言新手，想必你一定很期待一个大版本的发布会带来许多让人激动人心的语言特性。但是Go语言在这方面肯定会让你“失望”的。伴随着Go 1.0版本一起发布的Go1兼容性承诺给Go语言的规范加了一个“框框”，从Go 1.0到Go 1.15版本，Go语言对语言规范的变更屈指可数，因此资深Gopher在阅读Go版本的release notes时总是很自然的略过这一章节，因为这一章节通常都是如下面这样的描述： 这就是Go的设计哲学：简单！绝不轻易向语言中添加新语法元素增加语言的复杂性。除非是那些社区呼声很高并且是Go核心团队认可的。我们也可以将Go从1.0到Go 1.16这段时间称为“Go憋大招”的阶段，因为就在Go团队发布1.16版本之前不久，Go泛型提案正式被Go核心团队接受(Accepted)： 这意味着什么呢？这意味着在2022年2月份(Go 1.18)，Gopher们将迎来Go有史以来最大一次语言语法变更并且这种变更依然是符合Go1兼容性承诺的，这将避免Go社区出现Python3给Python社区带去的那种“割裂”。不过就像《“能力越大，责任越大” &#8211; Go语言之父详解将于Go 1.18发布的Go泛型》一文中Go语言之父Robert Griesemer所说的那样：泛型引入了抽象，但滥用抽象而没有解决实际问题将带来不必要的复杂性，请三思而后行! 离泛型的落地还有一年时间，就让我们耐心等待吧！ 二. Go对各平台/OS支持的变更 Go语言具有良好的可移植性，对各主流平台和OS的支持十分全面和及时，Go官博曾发布过一篇文章，简要列出了自Go1以来对各主流平台和OS的支持情况： Go1（2012年3月）支持原始系统(译注：上面提到的两种操作系统和三种架构)以及64位和32位x86上的FreeBSD、NetBSD和OpenBSD，以及32位x86上的Plan9。 Go 1.3（2014年6月）增加了对64位x86上Solaris的支持。 Go 1.4（2014年12月）增加了对32位ARM上Android和64位x86上Plan9的支持。 Go 1.5（2015年8月）增加了对64位ARM和64位PowerPC上的Linux以及32位和64位ARM上的iOS的支持。 Go 1.6（2016年2月）增加了对64位MIPS上的Linux，以及32位x86上的Android的支持。它还增加了32位ARM上的Linux官方二进制下载，主要用于RaspberryPi系统。 Go 1.7（2016年8月）增加了对的z系统（S390x）上Linux和32位x86上Plan9的支持。 Go 1.8（2017年2月）增加了对32位MIPS上Linux的支持，并且它增加了64位PowerPC和z系统上Linux的官方二进制下载。 Go 1.9（2017年8月）增加了对64位ARM上Linux的官方二进制下载。 Go 1.12（2018年2月）增加了对32位ARM上Windows10 IoT Core的支持，如RaspberryPi3。它还增加了对64位PowerPC上AIX的支持。 [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/go-1.16-1.png" alt="img{512x368}" /></p>
<p>辛丑牛年初七开工大吉的日子(2021.2.18)，Go核心开发团队为中国Gopher们献上了大礼 &#8211; <a href="https://mp.weixin.qq.com/s/7tYi-61teL0kBmWz7q2SGw">Go 1.16版本正式发布了</a>！国内Gopher可以在<a href="https://golang.google.cn/dl/">Go中国官网上</a>下载到Go 1.16在各个平台的安装包：</p>
<p><img src="https://tonybai.com/wp-content/uploads/go-1.16-2.png" alt="img{512x368}" /></p>
<p>2020年双12，Go 1.16进入freeze状态，即不再接受新feature，仅fix bug、编写文档和接受安全更新等，那时我曾写过一篇名为<a href="https://mp.weixin.qq.com/s/JzAQ3r9lDBad8PO6iAerqw">《Go 1.16新功能特性不完全前瞻》</a>的文章。当时Go 1.16的<a href="https://tip.golang.org/doc/go1.16">发布说明</a>尚处于早期草稿阶段，要了解Go 1.16功能特性都有哪些变化，只能结合当时的release note以及从<a href="https://github.com/golang/go/issues?q=is%3Aissue+milestone%3AGo1.16+is%3Aclosed">Go 1.16里程碑</a>中的issue列表中挖掘。</p>
<p>如今Go 1.16版本正式发布了，和当时相比，Go 1.16又有哪些变化呢？在这篇文章中，我们就来一起详细分析一下Go 1.16中那些值得关注的重要变化！</p>
<h3>一. 语言规范</h3>
<p>如果你是Go语言新手，想必你一定很期待一个大版本的发布会带来许多让人激动人心的语言特性。但是Go语言在这方面肯定会让你“失望”的。伴随着Go 1.0版本一起发布的<a href="https://tip.golang.org/doc/go1compat">Go1兼容性承诺</a>给Go语言的规范加了一个“框框”，从Go 1.0到<a href="https://mp.weixin.qq.com/s/B5onfyP7BPYCh_rMSBtfcQ">Go 1.15</a>版本，Go语言对语言规范的变更屈指可数，因此资深Gopher在阅读Go版本的release notes时总是很自然的略过这一章节，因为这一章节通常都是如下面这样的描述：</p>
<p><img src="https://tonybai.com/wp-content/uploads/go-1.16-3.png" alt="img{512x368}" /></p>
<p>这就是<a href="https://www.imooc.com/read/87/article/2321">Go的设计哲学：简单</a>！绝不轻易向语言中添加新语法元素增加语言的复杂性。除非是那些社区呼声很高并且是Go核心团队认可的。我们也可以将Go从1.0到Go 1.16这段时间称为“Go憋大招”的阶段，因为就在Go团队发布1.16版本之前不久，<a href="https://mp.weixin.qq.com/s/Cnko3hrrcFKpsfdlj3yXdQ">Go泛型提案</a>正式被Go核心团队接受(Accepted)：</p>
<p><img src="https://tonybai.com/wp-content/uploads/go-1.16-4.png" alt="img{512x368}" /></p>
<p>这意味着什么呢？这意味着在2022年2月份(Go 1.18)，Gopher们将迎来Go有史以来最大一次语言语法变更并且<strong>这种变更依然是符合Go1兼容性承诺的</strong>，这将避免Go社区出现Python3给Python社区带去的那种“割裂”。不过就像<a href="https://mp.weixin.qq.com/s/SMT40557JgQ9FjUkswznlA">《“能力越大，责任越大” &#8211; Go语言之父详解将于Go 1.18发布的Go泛型》</a>一文中Go语言之父<a href="https://github.com/griesemer">Robert Griesemer</a>所说的那样：<strong>泛型引入了抽象，但滥用抽象而没有解决实际问题将带来不必要的复杂性，请三思而后行</strong>! 离泛型的落地还有一年时间，就让我们耐心等待吧！</p>
<h3>二. Go对各平台/OS支持的变更</h3>
<p><strong>Go语言具有良好的<a href="https://tonybai.com/2017/06/27/an-intro-about-go-portability">可移植性</a>，对各主流平台和OS的支持十分全面和及时</strong>，Go官博曾<a href="https://mp.weixin.qq.com/s/FQWMwZnT8xmCGe1Ing_luQ">发布过一篇文章</a>，简要列出了自Go1以来对各主流平台和OS的支持情况：</p>
<ul>
<li>Go1（2012年3月）支持原始系统(译注：上面提到的两种操作系统和三种架构)以及64位和32位x86上的FreeBSD、NetBSD和OpenBSD，以及32位x86上的Plan9。</li>
<li>Go 1.3（2014年6月）增加了对64位x86上Solaris的支持。</li>
<li><a href="https://tonybai.com/2014/11/04/some-changes-in-go-1-4/">Go 1.4</a>（2014年12月）增加了对32位ARM上Android和64位x86上Plan9的支持。</li>
<li><a href="https://tonybai.com/2015/07/10/some-changes-in-go-1-5/">Go 1.5</a>（2015年8月）增加了对64位ARM和64位PowerPC上的Linux以及32位和64位ARM上的iOS的支持。</li>
<li><a href="https://tonybai.com/2016/02/21/some-changes-in-go-1-6/">Go 1.6</a>（2016年2月）增加了对64位MIPS上的Linux，以及32位x86上的Android的支持。它还增加了32位ARM上的Linux官方二进制下载，主要用于RaspberryPi系统。</li>
<li><a href="https://tonybai.com/2016/06/21/some-changes-in-go-1-7/">Go 1.7</a>（2016年8月）增加了对的z系统（S390x）上Linux和32位x86上Plan9的支持。</li>
<li><a href="https://tonybai.com/2017/02/03/some-changes-in-go-1-8/">Go 1.8</a>（2017年2月）增加了对32位MIPS上Linux的支持，并且它增加了64位PowerPC和z系统上Linux的官方二进制下载。</li>
<li><a href="https://tonybai.com/2017/07/14/some-changes-in-go-1-9/">Go 1.9</a>（2017年8月）增加了对64位ARM上Linux的官方二进制下载。</li>
<li><a href="https://tonybai.com/2019/03/02/some-changes-in-go-1-12">Go 1.12</a>（2018年2月）增加了对32位ARM上Windows10 IoT Core的支持，如RaspberryPi3。它还增加了对64位PowerPC上AIX的支持。</li>
<li><a href="https://tonybai.com/2020/03/08/some-changes-in-go-1-14">Go 1.14</a>（2019年2月）增加了对64位RISC-V上Linux的支持。</li>
</ul>
<p><a href="https://tonybai.com/2016/06/21/some-changes-in-go-1-7/">Go 1.7版本</a>中新增的<strong>go tool dist list</strong>命令还可以帮助我们快速了解各个版本究竟支持哪些平台以及OS的组合。下面是Go 1.16版本该命令的输出：</p>
<pre><code>$go tool dist list
aix/ppc64
android/386
android/amd64
android/arm
android/arm64
darwin/amd64
darwin/arm64
dragonfly/amd64
freebsd/386
freebsd/amd64
freebsd/arm
freebsd/arm64
illumos/amd64
ios/amd64
ios/arm64
js/wasm
linux/386
linux/amd64
linux/arm
linux/arm64
linux/mips
linux/mips64
linux/mips64le
linux/mipsle
linux/ppc64
linux/ppc64le
linux/riscv64
linux/s390x
netbsd/386
netbsd/amd64
netbsd/arm
netbsd/arm64
openbsd/386
openbsd/amd64
openbsd/arm
openbsd/arm64
openbsd/mips64
plan9/386
plan9/amd64
plan9/arm
solaris/amd64
windows/386
windows/amd64
windows/arm
</code></pre>
<p>通常我不太会过多关注每次Go版本发布时关于可移植性方面的内容，这次将可移植性单独作为章节主要是因为Go 1.16发布之前的<strong>Apple M1芯片事件</strong>！</p>
<p><img src="https://tonybai.com/wp-content/uploads/go-1.16-5.jpg" alt="img{512x368}" /></p>
<p>苹果公司再次放弃Intel x86芯片而改用自造的基于Arm64的M1芯片引发业界激烈争论。但现实是搭载Arm64 M1芯片的苹果笔记本已经大量上市，对于编程语言开发团队来说，能做的只有尽快支持这一平台。因此，Go团队给出了在Go 1.16版本中增加对Mac M1的原生支持。</p>
<p>在Go 1.16版本之前，Go也支持darwin/arm64的组合，但那更多是为了构建在iOS上运行的Go应用(利用<a href="https://github.com/golang/mobile">gomobile</a>)。</p>
<p>Go 1.16做了进一步的细分：将darwin/arm64组合改为apple M1专用；而构建在iOS上运行的Go应用则使用ios/arm64。同时，Go 1.16还增加了ios/amd64组合用于支持在MacOS(amd64)上运行的<a href="https://tip.golang.org/misc/ios/README">iOS模拟器中运行Go应用</a>。</p>
<p>另外还值得一提的是在OpenBSD上，Go应用的系统调用需要通过libc发起，而不能再绕过libc而直接使用汇编指令了，这是出于对未来OpenBSD的一些兼容性要求考虑才做出的决定。</p>
<h3>三. Go module-aware模式成为默认！</h3>
<p>在泛型落地前，Go module依旧是这些年Go语言改进的重点(虽不是语言规范特性)。在Go 1.16版本中，Go module-aware模式成为了默认模式(另一种则是传统的gopath模式)。module-aware模式成为默认意味着什么呢？意味着GO111MODULE的值默认为on了。</p>
<p>自从Go 1.11加入go module，不同go版本在GO111MODULE为不同值的情况下开启的构建模式几经变化，上一次go module-aware模式的行为有较大变更还是在<a href="https://mp.weixin.qq.com/s/Txqvanb17LYQYgohNiUHig">Go 1.13版本</a>中。这里将Go 1.13版本之前、Go 1.13版本以及Go 1.16版本在GO111MODULE为不同值的情况下的行为做一下对比，这样我们可以更好的理解go 1.16中module-aware模式下的行为特性，下面我们就来做一下比对：</p>
<table>
<thead>
<tr>
<th>GO111MODULE</th>
<th>&lt; Go 1.13</th>
<th>Go 1.13</th>
<th>Go 1.16</th>
</tr>
</thead>
<tbody>
<tr>
<td>on</td>
<td>任何路径下都开启module-aware模式</td>
<td>任何路径下都开启module-aware模式</td>
<td>【默认值】：任何路径下都开启module-aware模式</td>
</tr>
<tr>
<td>auto</td>
<td>【默认值】：使用GOPATH mode还是module-aware mode，取决于要构建的源码目录所在位置以及是否包含go.mod文件。如果要构建的源码目录不在以GOPATH/src为根的目录体系下，且包含go.mod文件(两个条件缺一不可)，那么使用module-aware mode；否则使用传统的GOPATH mode。</td>
<td>【默认值】：只要当前目录或父目录下有go.mod文件时，就开启module-aware模式，无论源码目录是否在GOPATH外面</td>
<td>只有当前目录或父目录下有go.mod文件时，就开启module-aware模式，无论源码目录是否在GOPATH外面</td>
</tr>
<tr>
<td>off</td>
<td>gopath模式</td>
<td>gopath模式</td>
<td>gopath模式</td>
</tr>
</tbody>
</table>
<p>我们看到在Go 1.16模式下，依然可以回归到gopath模式。但Go核心团队已经决定拒绝<a href="https://github.com/golang/go/issues/37755">“继续保留GOPATH mode”的提案</a>，并计划在Go 1.17版本中彻底取消gopath mode，仅保留go module-aware mode：</p>
<p><img src="https://tonybai.com/wp-content/uploads/go-1.16-6.png" alt="img{512x368}" /></p>
<p>虽然目前仍有项目没有转换到go module下，但根据调查，大多数项目已经选择拥抱go module并完成了转换工作，因此笔者认为即便Go 1.17真的取消了GOPATH mode，对整个Go社区的影响也不会太大了。</p>
<p>Go 1.16中，go module机制还有其他几个变化，这里逐一来看一下：</p>
<h4>1. go build/run命令不再自动更新go.mod和go.sum了</h4>
<p>为了能更清晰看出Go 1.16与之前版本的差异，我们准备了一个小程序：</p>
<pre><code>// github.com/bigwhite/experiments/blob/master/go1.16-examples/go-modules/helloworld/go.mod
module github.com/bigwhite/helloworld

go 1.16

// github.com/bigwhite/experiments/blob/master/go1.16-examples/go-modules/helloworld/helloworld.go
package main

import "github.com/sirupsen/logrus"

func main() {
    logrus.Println("Hello, World")
}
</code></pre>
<p>我们使用<a href="https://mp.weixin.qq.com/s/B5onfyP7BPYCh_rMSBtfcQ">go 1.15版本</a>构建一下该程序：</p>
<pre><code>$go build
go: finding module for package github.com/sirupsen/logrus
go: downloading github.com/sirupsen/logrus v1.8.0
go: found github.com/sirupsen/logrus in github.com/sirupsen/logrus v1.8.0

$cat go.mod
module github.com/bigwhite/helloworld

go 1.16

require github.com/sirupsen/logrus v1.8.0

$cat go.sum
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/magefile/mage v1.10.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.8.0 h1:nfhvjKcUMhBMVqbKHJlk5RPrrfYr/NMo3692g0dwfWU=
github.com/sirupsen/logrus v1.8.0/go.mod h1:4GuYW9TZmE769R5STWrRakJc4UqQ3+QQ95fyz7ENv1A=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
</code></pre>
<p>在Go 1.15版本中，go build会自动分析源码中的依赖，如果go.mod中没有对该依赖的require，则会自动添加require，同时会将go.sum中将相关包(特定版本)的校验信息写入。</p>
<p>我们将上述helloworld恢复到初始状态，再用go 1.16来build一次：</p>
<pre><code>$go build
helloworld.go:3:8: no required module provides package github.com/sirupsen/logrus; to add it:
    go get github.com/sirupsen/logrus
</code></pre>
<p>我们看到go build没有成功，而是给出错误：go.mod中没有对logrus的require，并给出添加对logrus的require的方法(go get github.com/sirupsen/logrus)。</p>
<p>我们就按照go build给出的提示执行go get：</p>
<pre><code>$go get github.com/sirupsen/logrus
go: downloading github.com/magefile/mage v1.10.0
go get: added github.com/sirupsen/logrus v1.8.0

$cat go.mod
module github.com/bigwhite/helloworld

go 1.16

require github.com/sirupsen/logrus v1.8.0 // indirect

$cat go.sum
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/magefile/mage v1.10.0 h1:3HiXzCUY12kh9bIuyXShaVe529fJfyqoVM42o/uom2g=
github.com/magefile/mage v1.10.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.8.0 h1:nfhvjKcUMhBMVqbKHJlk5RPrrfYr/NMo3692g0dwfWU=
github.com/sirupsen/logrus v1.8.0/go.mod h1:4GuYW9TZmE769R5STWrRakJc4UqQ3+QQ95fyz7ENv1A=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

$go build
//ok
</code></pre>
<p>我们看到go build并不会向go 1.15及之前版本那样做出有“副作用”的动作：自动修改go.mod和go.sum，而是提示开发人员显式通过go get来添加缺少的包/module，即便是<a href="https://tonybai.com/2019/06/03/the-practice-of-upgrading-major-version-under-go-module/">依赖包major版本升级</a>亦是如此。</p>
<p>从自动更新go.mod，到通过提供-mod=readonly选项来避免自动更新go.mod，再到Go 1.16的禁止自动更新go.mod，笔者认为这个变化是Go不喜“隐式转型”的一种延续，即尽量不支持任何可能让开发者产生疑惑或surprise的隐式行为（就像隐式转型），取而代之的是要用一种显式的方式去完成(就像必须显式转型那样)。</p>
<p>我们也看到在go 1.16中，添加或更新go.mod中的依赖，只有显式使用go get。go mod tidy依旧会执行对go.mod的清理，即也可以修改go.mod。</p>
<h4>2. 推荐使用go install安装Go可执行文件</h4>
<p>在gopath mode下，go install基本“隐身”了，它能做的事情基本都被go get“越俎代庖”了。在go module时代初期，go install更是没有了地位。但Go团队现在想逐步恢复go install的角色：安装Go可执行文件！在Go 1.16中，当go install后面的包携带特定版本号时，go install将忽略当前go.mod中的依赖信息而直接编译安装可执行文件：</p>
<pre><code>// go install回将gopls v0.6.5安装到GOBIN下
$go install golang.org/x/tools/gopls@v0.6.5
</code></pre>
<p>并且后续，Go团队会让go get将专注于分析依赖，并获取go包/module，更新go.mod/go.sum，而<a href="https://github.com/golang/go/issues/43684">不再具有安装可执行Go程序的行为能力</a>，这样go get和go install就会各司其职，Gopher们也不会再被两者的重叠行为所迷惑了。现在如果不想go get编译安装，可使用go get -d。</p>
<h4>3. 作废module的特定版本</h4>
<p>在<a href="https://mp.weixin.qq.com/s/fg84g4OzSgoHDGbvVDbMKg">《如何作废一个已发布的Go module版本，我来告诉你！》</a>一文中，我曾详细探讨了Go引入module后如何作废一个已发布的go module版本。当时已经知晓Go 1.16会在go.mod中增加<a href="https://github.com/golang/mod/commit/c0d644d00ab849f4506f17a98a5740bf0feff020">retract指示符</a>，因此也给出了在Go 1.16下retract一个module版本的原理和例子(基于当时的go tip)。</p>
<p>Go 1.16正式版在工具的输出提示方面做了进一步的优化，让开发人员体验更为友好。我们还是以一个简单的例子来看看在Go 1.16中作废一个module版本的过程吧。</p>
<p>在我的bitbucket账户下有一个名为m2的Go module(https://bitbucket.org/bigwhite/m2/)，当前它的版本为v1.0.0：</p>
<pre><code>// bitbucket.org/bigwhite/m2
$cat go.mod
module bitbucket.org/bigwhite/m2

go 1.15

$cat m2.go
package m2

import "fmt"

func M2() {
    fmt.Println("This is m2.M2 - v1.0.0")
}
</code></pre>
<p>我们在本地建立一个m2的消费者：</p>
<pre><code>// github.com/bigwhite/experiments/blob/master/go1.16-examples/go-modules/retract

$cat go.mod
module github.com/bigwhite/retractdemo

go 1.16

$cat main.go
package main

import "bitbucket.org/bigwhite/m2"

func main() {
    m2.M2()
}
</code></pre>
<p>运行这个消费者：</p>
<pre><code>$go run main.go
main.go:3:8: no required module provides package bitbucket.org/bigwhite/m2; to add it:
    go get bitbucket.org/bigwhite/m2
</code></pre>
<p>由于上面提到的原因，go run不会隐式修改go.mod，因此我们需要手工go get m2：</p>
<pre><code>$go get bitbucket.org/bigwhite/m2
go: downloading bitbucket.org/bigwhite/m2 v1.0.0
go get: added bitbucket.org/bigwhite/m2 v1.0.0
</code></pre>
<p>再来运行消费者，我们将看到以下运行成功的结果：</p>
<pre><code>$go run main.go
This is m2.M2 - v1.0.0
</code></pre>
<p>现在m2的作者对m2打了小补丁，版本升级到了v1.0.1。这时消费者通过go list命令可以看到m2的最新版本(前提：go proxy server上已经cache了最新的v1.0.1)：</p>
<pre><code>$go list -m -u all
github.com/bigwhite/retractdemo
bitbucket.org/bigwhite/m2 v1.0.0 [v1.0.1]
</code></pre>
<p>消费者可以通过go get将对m2的依赖升级到最新的v1.0.1：</p>
<pre><code>$go get bitbucket.org/bigwhite/m2@v1.0.1

go get: upgraded bitbucket.org/bigwhite/m2 v1.0.0 =&gt; v1.0.1
$go run main.go
This is m2.M2 - v1.0.1
</code></pre>
<p>m2作者收到issue，有人指出v1.0.1版本有安全漏洞，m2作者确认了该漏洞，但此时v1.0.1版已经发布并被缓存到各大go proxy server上，已经无法撤回。m2作者便想到了Go 1.16中引入的retract指示符，于是它在m2的go.mod用retract指示符做了如下更新：</p>
<pre><code>$cat go.mod
module bitbucket.org/bigwhite/m2

// 存在安全漏洞
retract v1.0.1

go 1.15
</code></pre>
<p>并将此次更新作为v1.0.2发布了出去！</p>
<p>之后，当消费者使用go list查看m2是否有最新更新时，便会看到retract提示：(前提：go proxy server上已经cache了最新的v1.0.2)</p>
<pre><code>$go list -m -u all
github.com/bigwhite/retractdemo
bitbucket.org/bigwhite/m2 v1.0.1 (retracted) [v1.0.2]
</code></pre>
<p>执行go get会收到带有更详尽信息的retract提示和问题解决建议：</p>
<pre><code>$go get .
go: warning: bitbucket.org/bigwhite/m2@v1.0.1: retracted by module author: 存在安全漏洞
go: to switch to the latest unretracted version, run:
    go get bitbucket.org/bigwhite/m2@latest
</code></pre>
<p>于是消费者按照提示执行go get bitbucket.org/bigwhite/m2@latest：</p>
<pre><code>$go get bitbucket.org/bigwhite/m2@latest
go get: upgraded bitbucket.org/bigwhite/m2 v1.0.1 =&gt; v1.0.2

$cat go.mod
module github.com/bigwhite/retractdemo

go 1.16

require bitbucket.org/bigwhite/m2 v1.0.2

$go run main.go
This is m2.M2 - v1.0.2
</code></pre>
<p>到此，retract的使命终于完成了！</p>
<h4>4. 引入GOVCS环境变量，控制module源码获取所使用的版本控制工具</h4>
<p>出于安全考虑，Go 1.16引入GOVCS环境变量，用于在go命令直接从代码托管站点获取源码时对所使用的版本控制工具进行约束，如果是从go proxy server获取源码，那么GOVCS将不起作用，因为go工具与go proxy server之间使用的是<a href="https://tip.golang.org/ref/mod#goproxy-protocol">GOPROXY协议</a>。</p>
<p>GOVCS的默认值为public:git|hg,private:all，即对所有公共module允许采用git或hg获取源码，而对私有module则不限制版本控制工具的使用。</p>
<p>如果要允许使用所有工具，可像下面这样设置GOVCS：</p>
<pre><code>GOVCS=*:all
</code></pre>
<p>如果要禁止使用任何版本控制工具去直接获取源码（不通过go proxy），那么可以像下面这样设置GOVCS:</p>
<pre><code>GOVCS=*:off
</code></pre>
<h4>5. 有关go module的文档更新</h4>
<p>自打<a href="https://mp.weixin.qq.com/s/PVxdtvSXgNpiD65TUo-TCg">Go 1.14版本</a>宣布go module生产可用后，Go核心团队在说服和帮助Go社区全面拥抱go module的方面不可谓不努力。在文档方面亦是如此，最初有关go module的文档仅局限于go build命令相关以及<a href="https://github.com/golang/go/wiki/Modules">有关go module的wiki</a>。随着go module日益成熟，go.mod格式的日益稳定，Go团队在1.16版本中还将go module相关文档升级到go reference的层次，与go language ref等并列：</p>
<p><img src="https://tonybai.com/wp-content/uploads/go-1.16-7.png" alt="img{512x368}" /></p>
<p>我们看到有关go module的ref文档包括：</p>
<ul>
<li><a href="https://tip.golang.org/ref/mod">Go Modules Reference</a> https://tip.golang.org/ref/mod</li>
<li><a href="https://tip.golang.org/doc/modules/gomod-ref">go.mod file reference</a> https://tip.golang.org/doc/modules/gomod-ref</li>
</ul>
<p>官方还编写了<a href="https://tip.golang.org/doc/#developing-modules">详细的Go module日常开发时的使用方法</a>，包括：开发与发布module、module发布与版本管理工作流、升级major号等。</p>
<p><img src="https://tonybai.com/wp-content/uploads/go-1.16-8.png" alt="img{512x368}" /></p>
<p><strong>建议每个gopher都要将这些文档仔细阅读一遍，以更为深入了解和使用go module</strong>。</p>
<h3>四. 编译器与运行时</h3>
<h4>1. runtime/metrics包</h4>
<p>在<a href="https://mp.weixin.qq.com/s/JzAQ3r9lDBad8PO6iAerqw">《Go 1.16新功能特性不完全前瞻》</a>一文中，我们提到过：Go 1.16 新增了runtime/metrics包，以替代runtime.ReadMemStats和debug.ReadGCStats输出runtime的各种度量数据，这个包更通用稳定，性能也更好。限于篇幅这里不展开，后续可能会以单独的文章讲解这个新包。</p>
<h4>2. GODEBUG环境变量支持跟踪包init函数的消耗</h4>
<p>GODEBUG=inittrace=1这个特性也保留在了Go 1.16正式版当中了。当GODEBUG环境变量包含inittrace=1时，Go运行时将会报告各个源代码文件中的init函数的执行时间和内存开辟消耗情况。我们用上面的helloworld示例(github.com/bigwhite/experiments/blob/master/go1.16-examples/go-modules/helloworld)来看看该特性的效果：</p>
<pre><code>$go build
$GODEBUG=inittrace=1 ./helloworld
init internal/bytealg @0.006 ms, 0 ms clock, 0 bytes, 0 allocs
init runtime @0.037 ms, 0.031 ms clock, 0 bytes, 0 allocs
init errors @0.29 ms, 0.005 ms clock, 0 bytes, 0 allocs
init math @0.31 ms, 0 ms clock, 0 bytes, 0 allocs
init strconv @0.33 ms, 0.002 ms clock, 32 bytes, 2 allocs
init sync @0.35 ms, 0.003 ms clock, 16 bytes, 1 allocs
init unicode @0.37 ms, 0.10 ms clock, 24568 bytes, 30 allocs
init reflect @0.49 ms, 0.002 ms clock, 0 bytes, 0 allocs
init io @0.51 ms, 0.003 ms clock, 144 bytes, 9 allocs
init internal/oserror @0.53 ms, 0 ms clock, 80 bytes, 5 allocs
init syscall @0.55 ms, 0.010 ms clock, 752 bytes, 2 allocs
init time @0.58 ms, 0.010 ms clock, 384 bytes, 8 allocs
init path @0.60 ms, 0 ms clock, 16 bytes, 1 allocs
init io/fs @0.62 ms, 0.002 ms clock, 16 bytes, 1 allocs
init internal/poll @0.63 ms, 0.001 ms clock, 64 bytes, 4 allocs
init os @0.65 ms, 0.089 ms clock, 4472 bytes, 20 allocs
init fmt @0.77 ms, 0.006 ms clock, 32 bytes, 2 allocs
init bytes @0.84 ms, 0.004 ms clock, 48 bytes, 3 allocs
init context @0.87 ms, 0 ms clock, 128 bytes, 4 allocs
init encoding/binary @0.89 ms, 0.002 ms clock, 16 bytes, 1 allocs
init encoding/base64 @0.90 ms, 0.015 ms clock, 1408 bytes, 4 allocs
init encoding/json @0.93 ms, 0.002 ms clock, 32 bytes, 2 allocs
init log @0.95 ms, 0 ms clock, 80 bytes, 1 allocs
init golang.org/x/sys/unix @0.96 ms, 0.002 ms clock, 48 bytes, 1 allocs
init bufio @0.98 ms, 0 ms clock, 176 bytes, 11 allocs
init github.com/sirupsen/logrus @0.99 ms, 0.009 ms clock, 312 bytes, 5 allocs
INFO[0000] Hello, World
</code></pre>
<p>以下面这行为例：</p>
<pre><code>init fmt @0.77 ms, 0.006 ms clock, 32 bytes, 2 allocs
</code></pre>
<ul>
<li>0.77ms表示的是自从程序启动后到fmt包init执行所过去的时间(以ms为单位)</li>
<li>0.006 ms clock表示fmt包init函数执行的时间(以ms为单位)</li>
<li>312 bytes表示fmt包init函数在heap上分配的内存大小；</li>
<li>5 allocs表示的是fmt包init函数在heap上执行内存分配操作的次数。</li>
</ul>
<h4>3. Go runtime默认使用MADV_DONTNEED</h4>
<p>Go 1.15版本时，我们可以通过GODEBUG=madvdontneed=1让Go runtime使用MADV_DONTNEED替代MADV_FREE达到更积极的将不用的内存释放给OS的效果(如果使用MADV_FREE，只有OS内存压力很大时，才会真正回收内存)，这将使得通过top查看到的常驻系统内存(RSS或RES)指标更实时也更真实反映当前Go进程对os内存的实际占用情况(仅使用linux)。</p>
<p>在Go 1.16版本中，Go runtime将MADV_DONTNEED作为默认值了，我们可以用一个小例子来对比一下这种变化：</p>
<pre><code>// github.com/bigwhite/experiments/blob/master/go1.16-examples/runtime/memalloc.go
package main

import "time"

func allocMem() []byte {
    b := make([]byte, 1024*1024*1) //1M
    return b
}

func main() {
    for i := 0; i &lt; 100000; i++ {
        _ = allocMem()
        time.Sleep(500 * time.Millisecond)
    }
}
</code></pre>
<p>我们在linux上使用go 1.16版本编译该程序，考虑到优化和inline的作用，我们在编译时关闭优化和内联：</p>
<pre><code>$go build -gcflags "-l -N" memalloc.go
</code></pre>
<p>接下来，我们分两次运行该程序，并使用top监控其RES指标值：</p>
<pre><code>$./memalloc
$ top -p 9273
  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND
 9273 root      20   0  704264   5840    856 S  0.0  0.3   0:00.03 memalloc
 9273 root      20   0  704264   3728    856 S  0.0  0.2   0:00.05 memalloc
 ... ...

$GODEBUG=madvdontneed=0 ./memalloc
$ top -p 9415

  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND
 9415 root      20   0  704264   5624    856 S  0.0  0.3   0:00.03 memalloc
 9415 root      20   0  704264   5624    856 S  0.0  0.3   0:00.05 memalloc
</code></pre>
<p>我们看到默认运行的memalloc(开启MADV_DONTNEED)，RES很积极的变化，当上一次显示5840，下一秒内存就被归还给OS，RES变为3728。而关闭MADV_DONTNEED（GODEBUG=madvdontneed=0）的memalloc，OS就会很lazy的回收内存，RES一直显示5624这个值。</p>
<h4>4. Go链接器的进一步进行<a href="https://golang.org/s/better-linker">现代化改造</a></h4>
<p>新一代Go链接器的更新计划从Go 1.15版本开始，在Go 1.15版本链接器的性能、资源占用、最终二进制文件大小等方面都有了一定幅度的优化提升。Go 1.16版本延续了这一势头：相比于Go 1.15，官方宣称(在linux上)性能有20%-25%的提升，资源占用下降5%-15%。更为直观的是编译出的二进制文件的size，我实测了一下文件大小下降10%以上：</p>
<pre><code>-rwxr-xr-x   1 tonybai  staff    22M  2 21 23:03 my-large-app-demo*
-rwxr-xr-x   1 tonybai  staff    25M  2 21 23:02 my-large-app-demo-go1.15*
</code></pre>
<p>并且和Go 1.15的链接器优化仅针对amd64平台和基于ELF格式的OS不同，<strong>这次的链接器优化已经扩展到所有平台和os组合上</strong>。</p>
<h3>五. 标准库</h3>
<h4>1. io/fs包</h4>
<p>Go 1.16标准库新增io/fs包，并定义了一个fs.File接口用于表示一个只读文件树(tree of file)的抽象。之所以要<a href="https://github.com/golang/go/issues/41190">加入io/fs包并新增fs.File接口</a>源于对<a href="https://github.com/golang/go/issues/41191">嵌入静态资源文件(embed static asset)的实现需求</a>。虽说实现embed功能特性是直接原因，但io/fs的加入也不是“临时起意”，早在很多年前的godoc实现时，对一个抽象的文件系统接口的需求就已经被提了出来并给出了实现：</p>
<p><img src="https://tonybai.com/wp-content/uploads/go-1.16-9.png" alt="" /></p>
<p>最终这份实现以godoc工具的<a href="https://github.com/golang/tools/tree/master/godoc/vfs">vfs包</a>的形式一直长期存在着。虽然它的实现有些复杂，抽象程度不够，但却对<a href="https://github.com/golang/proposal/blob/master/design/draft-iofs.md">io/fs包的设计</a>有着重要的参考价值。同时也部分弥补了<a href="https://github.com/golang/go/issues/14106">Rob Pike老爷子当年没有将os.File设计为interface的遗憾</a>，<a href="https://github.com/golang/go/issues/5636">Ian Lance Taylor 2013年提出的增加VFS层的想法</a>也一并得以实现。</p>
<p>io/fs包的两个最重要的接口如下：</p>
<pre><code>// $GOROOT/src/io/fs/fs.go

// An FS provides access to a hierarchical file system.
//
// The FS interface is the minimum implementation required of the file system.
// A file system may implement additional interfaces,
// such as ReadFileFS, to provide additional or optimized functionality.
type FS interface {
        // Open opens the named file.
        //
        // When Open returns an error, it should be of type *PathError
        // with the Op field set to "open", the Path field set to name,
        // and the Err field describing the problem.
        //
        // Open should reject attempts to open names that do not satisfy
        // ValidPath(name), returning a *PathError with Err set to
        // ErrInvalid or ErrNotExist.
        Open(name string) (File, error)
}

// A File provides access to a single file.
// The File interface is the minimum implementation required of the file.
// A file may implement additional interfaces, such as
// ReadDirFile, ReaderAt, or Seeker, to provide additional or optimized functionality.
type File interface {
        Stat() (FileInfo, error)
        Read([]byte) (int, error)
        Close() error
}
</code></pre>
<p>FS接口代表虚拟文件系统的最小抽象，File接口则是虚拟文件的最小抽象，我们可以基于这两个接口进行扩展以及对接现有的一些实现。io/fs包也给出了一些扩展FS的“样例”：</p>
<p><img src="https://tonybai.com/wp-content/uploads/go-1.16-10.png" alt="" /></p>
<p>这两个接口的设计也是“Go秉持定义小接口惯例”的延续(更多关于这方面的内容，可以参考我的专栏文章<a href="https://www.imooc.com/read/87/article/2425">《定义小接口是Go惯例》</a>)。</p>
<p>io/fs包的加入也契合了Go社区对vfs的需求，在Go团队决定加入io/fs并提交实现后，社区做出了积极的反应，在github上我们能看到好多为各类对象提供针对io/fs.FS接口实现的项目：</p>
<p><img src="https://tonybai.com/wp-content/uploads/go-1.16-11.png" alt="" /></p>
<p>io/fs.FS和File接口在后续Go演进过程中会像io.Writer和io.Reader一样成为Gopher们在操作类文件树时最爱的接口。</p>
<h4>2. embed包</h4>
<p>在<a href="https://mp.weixin.qq.com/s/JzAQ3r9lDBad8PO6iAerqw">《Go 1.16新功能特性不完全前瞻》</a>一文中我们曾重点说了Go 1.16将支持在Go二进制文件中嵌入静态文件并给出了一个在webserver中嵌入文本文件的例子：</p>
<pre><code>// github.com/bigwhite/experiments/blob/master/go1.16-examples/stdlib/embed/webserver/hello.txt
hello, go 1.16

// github.com/bigwhite/experiments/blob/master/go1.16-examples/stdlib/embed/webserver/main.go
package main

import (
         _  "embed"
    "net/http"
)

//go:embed hello.txt
var s string

func main() {
    http.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte(s))
    }))
    http.ListenAndServe(":8080", nil)
}
</code></pre>
<p>我们看到在这个例子，通过//go:embed hello.txt，我们可以轻易地将hello.txt的内容存储在包级变量s中，而s将作为每个http request的应答返回给客户端。</p>
<p><a href="https://github.com/golang/go/issues/41191">在Go二进制文件中嵌入静态资源文件</a>是Go核心团队对社区广泛需求的积极回应。在go 1.16以前，Go社区开源的类嵌入静态文件的项目不下十多个，在Russ Cox<a href="https://github.com/golang/proposal/blob/master/design/draft-embed.md">关于embed的设计草案</a>中，他就列了十多个：</p>
<ul>
<li>github.com/jteeuwen/go-bindata(主流实现)</li>
<li>github.com/alecthomas/gobundle</li>
<li>github.com/GeertJohan/go.rice</li>
<li>github.com/go-playground/statics</li>
<li>github.com/gobuffalo/packr</li>
<li>github.com/knadh/stuffbin</li>
<li>github.com/mjibson/esc</li>
<li>github.com/omeid/go-resources</li>
<li>github.com/phogolabs/parcello</li>
<li>github.com/pyros2097/go-embed</li>
<li>github.com/rakyll/statik</li>
<li>github.com/shurcooL/vfsgen</li>
<li>github.com/UnnoTed/fileb0x</li>
<li>github.com/wlbr/templify</li>
<li>perkeep.org/pkg/fileembed</li>
</ul>
<p>Go1.16原生支持嵌入并且给出一种开发者体验良好的实现方案，这对Go社区是一种极大的鼓励，也是Go团队重视社区声音的重要表现。</p>
<p>笔者认为embed机制是Go 1.16中玩法最多的一种机制，也是极具新玩法挖掘潜力的机制。在embed加入Go tip不久，很多Gopher就已经“脑洞大开”：</p>
<p>有通过embed嵌入版本号的：</p>
<pre><code>// github.com/bigwhite/experiments/blob/master/go1.16-examples/stdlib/embed/version/main.go
package main

import (
    _ "embed"
    "fmt"
    "strings"
)

var (
    Version string = strings.TrimSpace(version)
    //go:embed version.txt
    version string
)

func main() {
    fmt.Printf("Version %q\n", Version)
}

// github.com/bigwhite/experiments/blob/master/go1.16-examples/stdlib/embed/version/version.txt
v1.0.1
</code></pre>
<p>有通过embed打印自身源码的：</p>
<pre><code>// github.com/bigwhite/experiments/blob/master/go1.16-examples/stdlib/embed/printself/main.go
package main

import (
        _ "embed"
        "fmt"
)

//go:embed main.go
var src string

func main() {
        fmt.Print(src)
}
</code></pre>
<p>更是有<a href="https://blog.lawrencejones.dev/golang-embed/">将一个完整的、复杂的带有js支持的web站点直接嵌入到go二进制文件中的示例</a>，鉴于篇幅，这里就不一一列举了。</p>
<p>Go擅长于Web服务，而embed机制的引入粗略来看，可以大大简化web服务中资源文件的部署，估计这也是之前社区青睐各种静态资源文件嵌入项目的原因。embed估计也会成为Go 1.16中最被gopher们喜爱的功能特性。</p>
<p>不过embed机制的实现目前有如下一些局限：</p>
<ul>
<li>仅支持在包级变量前使用//go:embed指示符，还不支持在函数/方法内的局部变量上应用embed指示符（当然我们可以通过将包级变量赋值给局部变量来过渡一下）；</li>
<li>使用//go:embed指示符的包必须以空导入的方式导入embed包，二者是成对出现的，缺一不可；</li>
</ul>
<h4>3. net包的变化</h4>
<p>在Go 1.16之前，我们检测在一个已关闭的网络上进行I/O操作或在I/O完成前网络被关闭的情况，只能通过匹配字符串”use of closed network connection”的方式来进行。之前的版本没有针对这个错误定义“哨兵错误变量”(更多关于哨兵错误变量的内容，可以参考我的专栏文章<a href="https://www.imooc.com/read/87/article/2433">《别笑！这就是 Go 的错误处理哲学》</a>)，Go 1.16增加了ErrClosed这个“哨兵错误变量”，我们可以通过errors.Is(err, net.ErrClosed)来检测是否是上述错误情况。</p>
<h3>六. 小结</h3>
<p>从Go 1.16版本变更的功能特性中，我看到了Go团队更加重视社区的声音，这也是Go团队一直持续努力的目标。在最新的Go proposal review meeting的结论中，我们还看到了这样的一个<a href="https://github.com/golang/go/issues/43931">proposal</a>被accept：</p>
<p><img src="https://tonybai.com/wp-content/uploads/go-1.16-12.png" alt="" /></p>
<p>要知道这个proposal的提议是将在Go 1.18才会落地的泛型实现分支merge到Go项目master分支，也就是说在Go 1.17中就会包含“不会发布的”泛型部分实现，这在之前是不可能实现的(之前，新proposal必须有原型实现的分支，实现并经过社区测试与Go核心委员会评估后才会在特定版本merge到master分支)。虽说泛型的开发有其特殊情况，但能被accept，这恰证明了Go社区的声音在Go核心团队日益受到重视。</p>
<p><strong>如果你还没有升级到Go 1.16，那么现在正是时候</strong>。</p>
<p>本文中涉及的代码可以在<a href="https://github.com/bigwhite/experiments/tree/master/go1.16-examples">这里</a>下载。https://github.com/bigwhite/experiments/tree/master/go1.16-examples</p>
<hr />
<p><a href="https://mp.weixin.qq.com/s/jUqAL7hf2GmMun64BJufEA">“Gopher部落”知识星球</a>正式转正（从试运营星球变成了正式星球）！“gopher部落”旨在打造一个精品Go学习和进阶社群！高品质首发Go技术文章，“三天”首发阅读权，每年两期Go语言发展现状分析，每天提前1小时阅读到新鲜的Gopher日报，网课、技术专栏、图书内容前瞻，六小时内必答保证等满足你关于Go语言生态的所有需求！部落目前虽小，但持续力很强。在2021年上半年，部落将策划两个专题系列分享，并且是部落独享哦：</p>
<ul>
<li>Go技术书籍的书摘和读书体会系列</li>
<li>Go与eBPF系列</li>
</ul>
<p>考虑到部落尚处于推广期，这里仍然为大家准备了新人优惠券，虽然优惠幅度有所下降，但依然物超所值，早到早享哦！</p>
<p><img src="http://image.tonybai.com/img/202011/gopher-tribe-zsxq.png" alt="" /></p>
<p>Go技术专栏“<a href="https://www.imooc.com/read/87">改善Go语⾔编程质量的50个有效实践</a>”正在慕课网火热热销中！本专栏主要满足广大gopher关于Go语言进阶的需求，围绕如何写出地道且高质量Go代码给出50条有效实践建议，上线后收到一致好评！欢迎大家订阅！目前该技术专栏正在新春促销！关注我的个人公众号“iamtonybai”，发送“go专栏活动”即可获取专栏专属优惠码，可在订阅专栏时抵扣20元哦(2021.2月末前有效)。</p>
<p><img src="http://image.tonybai.com/img/202011/go-column-pgo-with-qr-and-text.png" alt="" /></p>
<p>我的网课“<a href="https://coding.imooc.com/class/284.html">Kubernetes实战：高可用集群搭建、配置、运维与应用</a>”在慕课网热卖中，欢迎小伙伴们订阅学习！</p>
<p><img src="https://tonybai.com/wp-content/uploads/k8s-practice-with-qr-and-text.png" 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>微信赞赏：<br />
<img src="https://tonybai.com/wp-content/uploads/wechat-zanshang-code-512x512.jpg" alt="img{512x368}" /></p>
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。</p>
<p style='text-align:left'>&copy; 2021, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2021/02/25/some-changes-in-go-1-16/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>2020年Go语言盘点：新冠大流行阻挡不了Go演进的步伐</title>
		<link>https://tonybai.com/2020/12/30/the-2020-review-of-go-programming-language/</link>
		<comments>https://tonybai.com/2020/12/30/the-2020-review-of-go-programming-language/#comments</comments>
		<pubDate>Wed, 30 Dec 2020 05:11:15 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[2020]]></category>
		<category><![CDATA[Apple]]></category>
		<category><![CDATA[defer]]></category>
		<category><![CDATA[Gartner]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[go-module]]></category>
		<category><![CDATA[go1.14]]></category>
		<category><![CDATA[go1.15]]></category>
		<category><![CDATA[go1.16]]></category>
		<category><![CDATA[go1.18]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[GOPATH]]></category>
		<category><![CDATA[GopherCon]]></category>
		<category><![CDATA[gopls]]></category>
		<category><![CDATA[GOPROXY]]></category>
		<category><![CDATA[HypeCycle]]></category>
		<category><![CDATA[init]]></category>
		<category><![CDATA[Interface]]></category>
		<category><![CDATA[linker]]></category>
		<category><![CDATA[M1]]></category>
		<category><![CDATA[panic]]></category>
		<category><![CDATA[pkg.go.dev]]></category>
		<category><![CDATA[RobertGriesemer]]></category>
		<category><![CDATA[RussCox]]></category>
		<category><![CDATA[Testing]]></category>
		<category><![CDATA[TIOBE]]></category>
		<category><![CDATA[tzdata]]></category>
		<category><![CDATA[vscode]]></category>
		<category><![CDATA[vscode-go]]></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=3059</guid>
		<description><![CDATA[2020，这一六十年一遇的庚子年的确“名不虚传”。在这一年发生了很多事，而最受瞩目的事情莫过于新冠疫情的全球大流行。疫情给全球的经济带来了近似毁灭性的打击，给人们的生命带来了极大威胁，给人们的生活也带来了很大痛苦及不确定性。好在这个糟糕的2020年马上就要过去了！相信此时此刻每个人心中都会有一句呐喊：“2020，快滚吧！”。 然而肆虐的新冠疫情并没有阻挡住Go语言前进的坚实步伐。在这艰难的一年中，在Go核心开发团队和Go社区的齐心协力下，Go同样取得了不俗的成绩，甚至在2020年3月(那时Go 1.14版本刚刚发布不到一个月)，Go在TIOBE的编程语言排行榜中还一度挤进前十(而2019年同期，Go仅位列18位)： 这恰说明Go语言的开发与推广工作得到了更多来自全球的开发者的认可。在这篇文章中，我们就来做一下2020年Go语言的盘点，看看在2020年围绕Go语言、Go社区和Go生态圈都发生了哪些有影响和有意义的事情。 1. 面对大流行，Go核心团队给出“定心丸” 大流行始于2020年1月的武汉，但真正的全球大流行则大致始于2020年3月。面对新冠全球大流行，Go核心开发团队于3月25日作出反应，在官博发表文章《Go, the Go Community, and the Pandemic》，迅速调整了Go语言2020年的演进计划，给出了大流行期间的工作原则： Go始终排在诸如个人和家庭健康与安全之类的基本问题之后； 调整全年Go技术会议的计划，推迟或改为线上举办虚拟技术大会，为全球Gopher提供获取这些会议最新信息的渠道服务； 为在线培训师、Go职位发布提供便利服务； 为新冠病毒提供帮助工作台：https://covid-oss-help.org/； 调整Go工作计划，缩减Go 1.15中包含的新特性和改进，但会遵循Go 1.15的发布时间表；重点支持gopls、pkg.go.dev的演进和优化。 Go核心开发团队的这份声明虽然简短，但却给Go社区吃了一颗“定心丸”，为Go语言在2020新冠大流行年中的稳步演进确定了节奏，指明了方向，奠定了基础。 2. Go在2020年值得关注的那些变化 2020一年，Go核心开发团队、社区和生态圈做了很多工作，但这里无法一一枚举，仅挑出一些重要的变化列在这里： 2020年2月26日，Go 1.14版本发布。主要的变动点包括： 嵌入接口的方法集可重叠； 基于系统信号机制实现了异步抢占式的goroutine调度； defer性能得以继续优化，理论上有30%的性能提升； go module已经生产就绪，并支持subversion源码仓库； 重新实现了运行时的timer； testing包的T和B类型都增加了自己的Cleanup方法。 2020年4月20日，发布2019年Go开发者调查结果： 参与2019开发者调查的gopher数量几乎为2018年的2倍，达到10,975人； 大多数受访者每天都在使用Go，而且这个数字每年都有上升的趋势； Go的使用仍然集中在科技公司，但Go越来越多地出现在更广泛的行业中，如金融和媒体； 调查的大部分指标的同比值都很稳定； 受访者正在使用Go来解决类似的问题，特别是构建API/RPC服务和CLI，和他们工作的组织规模大小关系不大； 大多数团队试图快速更新到最新的Go版本；当第三方供应商迟迟不支持当前的Go版本时，就会给开发者造成采用障碍； 现在Go生态系统中几乎所有人都在使用go module，但围绕包管理的一些混乱仍然存在； 需要改进的高优先级领域包括调试、go module使用以及与云服务交互的体验改善； VS Code和GoLand的使用量持续增加；现在每4个受访者中就有3个首选它们。 2020年6月，vscode-go扩展(vscode上的go标准插件)将主代码库从github.com/microsoft/vscode-go迁移到github.com/golang/vscode-go，成为Go官方项目的一部分。 同在2020年6月，pkg.go.dev网站开源！该网站是Go团队在Go社区建设方面做出的主要工作，开源后的pkg.go.dev将接收更多来自社区的想法和改进意见，比如：11月，pkg.go.dev就发布了新版页面设计；原godoc.org的请求也被重定向到pkg.go.dev(广大gopher可能需要一段时间来适应这种改变)。 2020年8月，Go 1.15版本发布，其主要的变动点包括： GOPROXY新增以管道符为分隔符的代理列表值； module cache的存储路径可设置; 改善派生自原生类型的自定义类型变量在panic时的输出形式； 将小整数([0,255])转换为interface类型值时将不会额外分配内存； [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/the-2020-review-of-go-programming-language-1.png" alt="img{512x368}" /></p>
<p>2020，这一六十年一遇的庚子年的确“名不虚传”。在这一年发生了很多事，而最受瞩目的事情莫过于<strong>新冠疫情的全球大流行</strong>。疫情给全球的经济带来了近似毁灭性的打击，给人们的生命带来了极大威胁，给人们的生活也带来了很大痛苦及不确定性。好在<strong>这个糟糕的2020年马上就要过去了</strong>！相信此时此刻每个人心中都会有一句呐喊：“<strong>2020，快滚吧</strong>！”。</p>
<p>然而肆虐的新冠疫情并没有阻挡住Go语言前进的坚实步伐。在这艰难的一年中，在Go核心开发团队和Go社区的齐心协力下，Go同样取得了<strong>不俗的成绩</strong>，甚至在2020年3月(那时<a href="https://mp.weixin.qq.com/s/PVxdtvSXgNpiD65TUo-TCg">Go 1.14版本</a>刚刚发布不到一个月)，Go在<a href="https://tiobe.com/tiobe-index/">TIOBE的编程语言排行榜</a>中还一度<a href="https://my.oschina.net/u/4593547/blog/4453355">挤进前十</a>(而2019年同期，Go仅位列18位)：</p>
<p><img src="https://tonybai.com/wp-content/uploads/the-2020-review-of-go-programming-language-4.jpg" alt="img{512x368}" /></p>
<p>这恰说明Go语言的开发与推广工作得到了更多来自全球的开发者的认可。在这篇文章中，我们就来做一下2020年Go语言的盘点，看看在2020年围绕Go语言、Go社区和Go生态圈都发生了哪些有影响和有意义的事情。</p>
<h3>1. 面对大流行，Go核心团队给出“定心丸”</h3>
<p>大流行始于2020年1月的武汉，但真正的全球大流行则大致始于2020年3月。面对新冠全球大流行，Go核心开发团队于3月25日作出反应，在官博发表文章<a href="https://blog.golang.org/pandemic">《Go, the Go Community, and the Pandemic》</a>，迅速调整了Go语言2020年的演进计划，给出了大流行期间的工作原则：</p>
<ul>
<li>Go始终排在诸如个人和家庭健康与安全之类的基本问题之后；</li>
<li>调整全年Go技术会议的计划，推迟或改为线上举办虚拟技术大会，为全球Gopher提供获取这些会议最新信息的渠道服务；</li>
<li>为在线培训师、Go职位发布提供便利服务；</li>
<li>为新冠病毒提供<a href="https://covid-oss-help.org/">帮助工作台</a>：https://covid-oss-help.org/；</li>
<li>调整Go工作计划，缩减<a href="https://mp.weixin.qq.com/s/B5onfyP7BPYCh_rMSBtfcQ">Go 1.15</a>中包含的新特性和改进，但会遵循Go 1.15的发布时间表；重点支持gopls、pkg.go.dev的演进和优化。</li>
</ul>
<p>Go核心开发团队的这份声明虽然简短，但却给Go社区吃了一颗“定心丸”，为Go语言在2020新冠大流行年中的稳步演进确定了节奏，指明了方向，奠定了基础。</p>
<h3>2. Go在2020年值得关注的那些变化</h3>
<p>2020一年，Go核心开发团队、社区和生态圈做了很多工作，但这里无法一一枚举，仅挑出一些重要的变化列在这里：</p>
<ul>
<li>
<p>2020年2月26日，Go 1.14版本发布。主要的变动点包括：</p>
<ul>
<li>嵌入接口的方法集可重叠；</li>
<li>基于系统信号机制实现了异步抢占式的goroutine调度；</li>
<li>defer性能得以继续优化，理论上有30%的性能提升；</li>
<li>go module已经生产就绪，并支持subversion源码仓库；</li>
<li>重新实现了运行时的timer；</li>
<li>testing包的T和B类型都增加了自己的<strong>Cleanup</strong>方法。</li>
</ul>
</li>
<li>
<p>2020年4月20日，发布<a href="https://blog.golang.org/survey2019-results">2019年Go开发者调查结果</a>：</p>
<ul>
<li>参与2019开发者调查的gopher数量几乎为2018年的2倍，达到10,975人；</li>
<li>大多数受访者每天都在使用Go，而且这个数字每年都有上升的趋势；</li>
<li>Go的使用仍然集中在科技公司，但Go越来越多地出现在更广泛的行业中，如金融和媒体；</li>
<li>调查的大部分指标的同比值都很稳定；</li>
<li>受访者正在使用Go来解决类似的问题，特别是构建API/RPC服务和CLI，和他们工作的组织规模大小关系不大；</li>
<li>大多数团队试图快速更新到最新的Go版本；当第三方供应商迟迟不支持当前的Go版本时，就会给开发者造成采用障碍；</li>
<li>现在Go生态系统中几乎所有人都在使用<a href="https://mp.weixin.qq.com/s/RThCEQOdytQxwrMP7XRTRw">go module</a>，但围绕包管理的一些混乱仍然存在；</li>
<li>需要改进的高优先级领域包括调试、go module使用以及与云服务交互的体验改善；</li>
<li>VS Code和GoLand的使用量持续增加；现在每4个受访者中就有3个首选它们。</li>
</ul>
</li>
<li>
<p>2020年6月，vscode-go扩展(vscode上的go标准插件)将主代码库从github.com/microsoft/vscode-go迁移到github.com/golang/vscode-go，成为Go官方项目的一部分。</p>
</li>
<li>
<p>同在2020年6月，pkg.go.dev网站开源！该网站是Go团队在Go社区建设方面做出的主要工作，开源后的pkg.go.dev将接收更多来自社区的想法和改进意见，比如：11月，<a href="https://blog.golang.org/pkgsite-redesign">pkg.go.dev就发布了新版页面设计</a>；<a href="https://blog.golang.org/godoc.org-redirect">原godoc.org的请求也被重定向到pkg.go.dev</a>(广大gopher可能需要一段时间来适应这种改变)。</p>
</li>
<li>
<p>2020年8月，<a href="https://mp.weixin.qq.com/s/B5onfyP7BPYCh_rMSBtfcQ">Go 1.15版本发布</a>，其主要的变动点包括：</p>
<ul>
<li>GOPROXY新增以管道符为分隔符的代理列表值；</li>
<li>module cache的存储路径可设置;</li>
<li>改善派生自原生类型的自定义类型变量在panic时的输出形式；</li>
<li>将小整数([0,255])转换为interface类型值时将不会额外分配内存；</li>
<li>加入更现代化的链接器(linker)，新链接器的性能要提高20%，内存占用减少30%；</li>
<li>增加tzdata包。</li>
</ul>
</li>
<li>
<p>2020年11月初，全球最具影响力的Go语言技术大会<a href="https://www.gophercon.com/">GopherCon 2020</a>在线上举行！Austin Clements详细讲解了Go 1.14加入的基于系统信号的抢占式调度器；Go语言之父之一的Robert Griesemer讲解了<a href="https://mp.weixin.qq.com/s/SMT40557JgQ9FjUkswznlA">Go泛型当前的状态以及未来的计划</a>。会后Russ Cox确认了Go团队将在Go 1.18版本中<a href="https://mp.weixin.qq.com/s/14WeOQBdezWTC5OqQrJtfg">加入Go泛型(类型参数)</a>作为试验特性；</p>
</li>
<li>
<p>2020年11月10日，Russ Cox代表Go核心开发团队发文庆祝<a href="https://mp.weixin.qq.com/s/woQeEQUhOLJ7KSE5rm5q6g"><strong>Go语言发布11周年</strong></a>，在文中他回顾了Go这一年来的收获以及对2021年<a href="https://mp.weixin.qq.com/s/JzAQ3r9lDBad8PO6iAerqw">Go 1.16</a>和Go 1.17的展望。文中他还提到了GOPATH的历史使命即将结束，Go将开启全面module-aware模式的Go工具链时代！(下图来自推特)：</p>
</li>
</ul>
<p><img src="https://tonybai.com/wp-content/uploads/the-2020-review-of-go-programming-language-5.jpeg" alt="img{512x368}" /></p>
<ul>
<li>
<p>2020年12月中旬，Go 1.16beta1发布。在<a href="https://mp.weixin.qq.com/s/JzAQ3r9lDBad8PO6iAerqw">Go 1.16</a>中，Go将原生提供对Apple M1芯片(darwin/arm64)的支持；同时，在Go 1.16中go module将成为默认包依赖管理机制；Go 1.16还提供了支持在Go二进制文件中嵌入静态文件的官方原生方案，支持对init函数的执行时间和内存消耗的跟踪，链接器性能得到进一步优化等。</p>
</li>
<li>
<p>2020年12月16日，gopls <a href="https://github.com/golang/tools/releases/tag/gopls%2Fv0.6.0">v0.6.0</a>发布。同期，vscode-go也正<a href="https://github.com/golang/vscode-go/issues/1037">计划将gopls作为默认语言服务器</a>。</p>
</li>
</ul>
<h3>3. Go语言当前的状态：已来到“稳定爬升的光明期”</h3>
<p>今年笔者在知乎上滞留的时间比往年要长一些，看到很多人问与Go相关的一些问题，大致都是询问有关Go语言前景的，比如：</p>
<ul>
<li><a href="https://www.zhihu.com/question/379439622/answer/1618633268">2020年以后是Go语言的天下吗？</a></li>
<li><a href="https://www.zhihu.com/question/386183447/answer/1575697918">2020年各个大厂内部Go语言开发环境是怎样的呢？有什么可以分享的经验吗？</a></li>
<li><a href="https://www.zhihu.com/question/431762724/answer/1593984796">Go语言前景如何？</a></li>
<li><a href="https://www.zhihu.com/question/435974651/answer/1648652343">2021年后哪个后端编程语言会越来越流行？</a></li>
</ul>
<p>无论上述问题的题目有何不同，其本质的疑问都是“<strong>Go语言前景/钱景如何，值不值得投入去学习?</strong>”。那么是否存在一种成熟的方法能相对客观地描会出Go语言的发展态势并能对未来Go的走势做出指导呢？我想Gartner的<a href="https://www.gartner.com/en/research/methodologies/gartner-hype-cycle"><strong>技术成熟度曲线（The Hype Cycle）</strong></a>或许可以一试。</p>
<p>我们知道Gartner的技术成熟度曲线又叫技术循环曲线，是企业用来评估新科技是否要采用或采用时机的一种可视化方法，它利用时间轴与该技术在市面上的可见度(媒体曝光度)决定要不要采用以及何时该种新科技，下面就是一条典型的技术成熟度曲线的形状：</p>
<p><img src="https://tonybai.com/wp-content/uploads/the-2020-review-of-go-programming-language-6.png" alt="img{512x368}" /></p>
<p>同理，将该技术成熟度曲线应用于某种编程语言，比如Go，我们就可以用它来判断该编程语言所处的成熟阶段以辅助决定要不要采用以及何时采用该门语言。我们从知名的<a href="https://www.tiobe.com/tiobe-index/go/">TIOBE编程语言指数排行榜获取Go从2009年开源以来至今的指数曲线图</a>，并且根据<a href="https://tip.golang.org/doc/devel/release.html">Go版本发布史</a>在图中标记出了各个时段的Go发布版本：</p>
<p><img src="https://tonybai.com/wp-content/uploads/the-2020-review-of-go-programming-language-2.png" alt="img{512x368}" /></p>
<p>对比上面的Gartner成熟度曲线，相信你肯定有所发现。我们共同来解释一下：</p>
<ul>
<li>Go语言从2009年宣布开源以来，经历了两次“高峰”：一次是2009年刚刚宣布开源后，一次是在Go1.7~Go 1.9期间。显然，第一次的高峰实际上是一个“假高峰”，那时的Go连1.0版本都尚未发布，我们完全可以将其“剔除”掉。</li>
<li>从图中来看，Go语言的技术萌芽期是比较长的，从2012年的Go 1.0一直持续到2015年的<a href="https://tonybai.com/2015/07/10/some-changes-in-go-1-5/">Go 1.5</a>；</li>
<li><a href="https://tonybai.com/2015/07/10/some-changes-in-go-1-5/">Go 1.5版本</a>的自举以及Go垃圾回收延迟的大幅下降“引爆”了Go的“媒体曝光度”，Go技术的“期望膨胀期”开始，经历从<a href="https://tonybai.com/2016/02/21/some-changes-in-go-1-6/">Go 1.6</a>到<a href="https://tonybai.com/2017/07/14/some-changes-in-go-1-9/">Go 1.9版本</a>的发布后，业界对Go的期望达到了峰值；</li>
<li>从Go 1.10开始，Go似乎变得“仿徨”起来，原本期望Go“一统天下”的愿望没能实现，全面出击失败后，期望的落空导致了人们对<a href="https://mp.weixin.qq.com/s/TJsEvqPA00qvGSRr6a8Emg">Go产生了“功能孱弱劣势”的印象</a>，于是Go在Go 1.11发布前跌到了“泡沫破裂”的谷底；</li>
<li><a href="https://tonybai.com/2018/11/19/some-changes-in-go-1-11">Go 1.11</a>引入了<a href="https://tonybai.com/2019/06/03/the-practice-of-upgrading-major-version-under-go-module/">Go module</a>，给社区解决<a href="https://tonybai.com/2019/09/21/brief-history-of-go-package-management">Go包依赖问题</a>打了一剂强心剂，于是Go又开始了缓慢的爬升；</li>
<li>从TIOBE提供的曲线来看，<a href="https://tonybai.com/2019/03/02/some-changes-in-go-1-12">Go 1.12</a>到<a href="https://mp.weixin.qq.com/s/B5onfyP7BPYCh_rMSBtfcQ">Go 1.15版本</a>的发布让我们有信心认为Go已经进入了“稳步爬升的光明期”。</li>
</ul>
<p>到此，我相信知乎上的很多问题都应该迎刃而解了，剩下的只是<a href="https://mp.weixin.qq.com/s/2rsBJbz55nDEDax6vqKE5w">如何学习Go的细节</a>和<a href="https://mp.weixin.qq.com/s/RThCEQOdytQxwrMP7XRTRw">如何Go进阶</a>了。</p>
<p>不过可能还有很多朋友会问，Go何时能达到<strong>实质生产高峰期</strong>呢？这个问题真不好回答。但进入了“稳步爬升的光明期”后的Go到达实质生产高峰期只是一个时间问题了，也许2022年初发布的<a href="https://mp.weixin.qq.com/s/SMT40557JgQ9FjUkswznlA">支持Go泛型特性</a>的Go 1.18版本会快速推动Go向更高阶段进发！</p>
<h3>4. 展望Go的2021：继续蓄力，迎接下一个“引爆点”</h3>
<p>促使Go回到“稳步爬升光明期”的go module机制将在2021年年初正式发布的Go 1.16中成为默认包依赖管理机制。而<a href="https://github.com/golang/go/milestone/145">Go 1.16版本</a>也已经处于特性冻结并发布了beta1版本的阶段，其更多特性可以参考我的<a href="https://mp.weixin.qq.com/s/JzAQ3r9lDBad8PO6iAerqw">“Go 1.16新功能特性不完全前瞻”</a>一文。</p>
<p>将于2021年八月发布的<a href="https://github.com/golang/go/milestone/163">Go 1.17的里程碑</a>已经建立, 从里程碑的内容来看，已基本确定加入的功能特性和改进包括：</p>
<ul>
<li>针对x86-64的新的<a href="https://github.com/golang/go/issues/40724">基于寄存器的调用约定</a>（不破坏现有程序集！），这将使程序与主流语言的ABI模型保持一致，并且整体更快；</li>
<li><a href="https://github.com/golang/go/issues/41184">加入build指示器新语法</a>：<strong>//go:build</strong>；</li>
<li><a href="https://github.com/golang/go/issues/395">一个十多年前的issue</a>被Go团队accept：使用<strong>(*[4]int)(x)</strong>语法将切片x转型为一个数组类型指针(*[4]int)。 </li>
</ul>
<p>当然Go 1.17还会持续优化链接器，更多功能特性和改进还待Go团队策划补充。</p>
<p>而万众期待的Go泛型依然会继续打磨，从2016年Ian Lance Taylor提出<a href="https://github.com/golang/proposal/blob/master/design/15292-generics.md">“Go should have generics”</a>的设计草案以来，Go泛型草案至今已经讨论了4年多了，这再次证明了Go团队对于这类会显著增加Go复杂性的特性是多么地“慎之又慎”。虽然Go团队初步确定了在Go 1.18版本中将Go泛型（类型参数）落地，但近期Go项目中关于Go泛型的<a href="https://github.com/golang/go/issues/15292">主issue：proposal: spec: generic programming facilities</a>中仍然有不少反对的声音。Go团队在<strong>“继续保持Go简单”</strong>的道路上真是任重道远啊！</p>
<p>总之，2021年，Go将继续稳步爬升，也许爬的并没有那么快，但在我看来，这是在积蓄力量，等待着下一个引爆点。</p>
<h3>5. 小结</h3>
<p>Go在新冠疫情大流行的历史时期依旧步行稳健，为下一个“引爆点”积极蓄力。Go在自己传统领域依旧存在明显优势，比如：企业级应用、基础设施、中间件、微服务API、命令行应用等，并且在这些领域取得了越来越多开发者的青睐。</p>
<p>Go在其他领域也有“意外收获”，比如：<a href="https://www.imperva.com/blog/python-and-go-top-the-chart-of-2019s-most-popular-hacking-tools/">在黑客工具领域，Go已经逐渐威胁着Python的龙头地位了</a>，显然<a href="https://www.imooc.com/read/87/article/2321">语法简单</a>、<a href="https://www.imooc.com/read/87/article/2340">原生并发</a>、<a href="https://www.imooc.com/read/87/article/2341">自带“电池”</a>、轻松跨平台的编译以及编译为独立二进制文件的Go与黑客的需求十分契合。不过，在安全领域成为了进攻“武器”，这想必是Go设计者们所意料不到的。</p>
<h3>6. 福利！2020年本博客最受欢迎Go相关文章TOP10</h3>
<ul>
<li><a href="https://mp.weixin.qq.com/s/mkpnR8LYHtauBGzQC-SglQ">Go新泛型设计方案详解</a></li>
<li><a href="https://mp.weixin.qq.com/s/TJsEvqPA00qvGSRr6a8Emg">Go语言有哪些“劣势”</a></li>
<li><a href="https://mp.weixin.qq.com/s/woQeEQUhOLJ7KSE5rm5q6g">Go，11周年</a></li>
<li><a href="https://mp.weixin.qq.com/s/JzAQ3r9lDBad8PO6iAerqw">Go 1.16新功能特性不完全前瞻</a></li>
<li><a href="https://mp.weixin.qq.com/s/PVxdtvSXgNpiD65TUo-TCg">Go 1.14中值得关注的几个变化</a></li>
<li><a href="https://mp.weixin.qq.com/s/B5onfyP7BPYCh_rMSBtfcQ">Go 1.15中值得关注的几个变化</a></li>
<li><a href="https://mp.weixin.qq.com/s/zrM0I-CsEujAm6ho6AD79g">像跟踪分布式服务调用那样跟踪Go函数调用链</a></li>
<li><a href="https://mp.weixin.qq.com/s/2rsBJbz55nDEDax6vqKE5w">系统学习Go语言，有这几本书就够了</a></li>
<li><a href="https://mp.weixin.qq.com/s/rsDC-6paC5zN4sepWd5LqQ">通过实例深入理解sync.Map的工作原理</a></li>
<li><a href="https://mp.weixin.qq.com/s/RThCEQOdytQxwrMP7XRTRw">Go专栏“改善Go语言编程质量的50个有效实践”上线了</a></li>
</ul>
<hr />
<p><strong>Gopher部落</strong>知识星球已正式转正了！高品质首发Go技术文章，“三天”首发阅读权，每年两期Go语言发展现状分析，每天提前1小时阅读到新鲜的Gopher日报，网课、技术专栏、图书内容前瞻，六小时内必答保证等满足你关于Go语言生态的所有需求！星球首开，福利自然是少不了的！2020年年底之前，8.8折加入星球，下方图片扫起来吧，先到先得哦！</p>
<p><img src="http://image.tonybai.com/img/202011/gopher-tribe-zsxq.png" alt="" /></p>
<p>Go技术专栏“<a href="https://www.imooc.com/read/87">改善Go语⾔编程质量的50个有效实践</a>”正在慕课网火热热销中！本专栏主要满足广大gopher关于Go语言进阶的需求，围绕如何写出地道且高质量Go代码给出50条有效实践建议，上线后收到一致好评！欢迎大家订阅！</p>
<p><img src="http://image.tonybai.com/img/202011/go-column-pgo-with-qr-and-text.png" alt="" /></p>
<p>我的网课“<a href="https://coding.imooc.com/class/284.html">Kubernetes实战：高可用集群搭建、配置、运维与应用</a>”在慕课网热卖中，欢迎小伙伴们订阅学习！</p>
<p><img src="https://tonybai.com/wp-content/uploads/k8s-practice-with-qr-and-text.png" alt="img{512x368}" /></p>
<p><a href="https://tonybai.com/">我爱发短信</a>：企业级短信平台定制开发专家 https://tonybai.com/<br />
smspush : 可部署在企业内部的定制化短信平台，三网覆盖，不惧大并发接入，可定制扩展； 短信内容你来定，不再受约束, 接口丰富，支持长短信，签名可选。</p>
<p>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>微信赞赏：<br />
<img src="https://tonybai.com/wp-content/uploads/wechat-zanshang-code-512x512.jpg" alt="img{512x368}" /></p>
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。</p>
<p style='text-align:left'>&copy; 2020, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2020/12/30/the-2020-review-of-go-programming-language/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Go 1.16新功能特性不完全前瞻</title>
		<link>https://tonybai.com/2020/12/12/a-forward-look-to-new-feature-of-go-1-16/</link>
		<comments>https://tonybai.com/2020/12/12/a-forward-look-to-new-feature-of-go-1-16/#comments</comments>
		<pubDate>Sat, 12 Dec 2020 03:37:41 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[Apple]]></category>
		<category><![CDATA[arm64]]></category>
		<category><![CDATA[bson]]></category>
		<category><![CDATA[Cgo]]></category>
		<category><![CDATA[Darwin]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[go.mod]]></category>
		<category><![CDATA[go1.15]]></category>
		<category><![CDATA[go1.16]]></category>
		<category><![CDATA[go:embed]]></category>
		<category><![CDATA[GOARCH]]></category>
		<category><![CDATA[GODEBUG]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[gomodule]]></category>
		<category><![CDATA[GOOS]]></category>
		<category><![CDATA[Gopher部落]]></category>
		<category><![CDATA[init]]></category>
		<category><![CDATA[iOS]]></category>
		<category><![CDATA[json]]></category>
		<category><![CDATA[linker]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[M1]]></category>
		<category><![CDATA[Macbook]]></category>
		<category><![CDATA[metrics]]></category>
		<category><![CDATA[retract]]></category>
		<category><![CDATA[RISC-V]]></category>
		<category><![CDATA[runtime]]></category>
		<category><![CDATA[structtag]]></category>
		<category><![CDATA[Unicode]]></category>
		<category><![CDATA[XML]]></category>
		<category><![CDATA[知识星球]]></category>
		<category><![CDATA[结构体]]></category>
		<category><![CDATA[运行时]]></category>
		<category><![CDATA[链接器]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=3026</guid>
		<description><![CDATA[2020年最后一个购物狂欢，双十二购物节“Gopher部落”知识星球推出双十二优惠！本年度最低折扣仅限今天一天。笔者建立“Gopher部落”旨在建立一个高质量的Go语言技术精品社区，持续不断的高质量技术资料分享，让加入的星友每天都有新收获！欢迎大家加入！ Go 1.16将于2021年2月发布。目前已经进入freeze状态，即不再接受新feature，仅fix bug、编写文档和接受安全更新等。 目前Go 1.16的发布说明尚处于早期草稿阶段，但Go团队成员正在致力于编写发布说明。Go 1.16的完全特性列表说明还得等真正发布前才能得到。如今要了解Go 1.16功能特性都有哪些变化，只能结合现有的release note以及从Go 1.16里程碑中的issue列表中挖掘。 下面就“挖掘”到的Go 1.16重要功能特性变化做简要的且不完全的前瞻。 1. 支持Apple Silicon M1芯片 Apple Silicon M1芯片Macbook的发布让Go团队紧急为Go 1.16增加对M1的支持。如果要跨平台编译，只需设定GOOS=darwin, GOARCH=arm64即可构建出可以在搭载M1芯片的Macbook上运行的Go应用。 同时Go 1.16还增加了对ios/amd64的支持，主要是为了支持在amd64架构上的MacOS上运行ios模拟器。 2. RISC-V架构支持cgo和-buildmode=pie RISC-V架构很可能是未来5-10年挑战ARM的最主要架构，Go语言持续加大对RISC-V架构的支持，在Go 1.16中对linux/riscv64又增加了cgo支持以及-buildmode=pie。不过目前对risc-v仍仅限于linux os。 3. 有关go module的变化 module-aware模式成为默认状态。如要回到gopath mode，将GO111MODULE设置为auto； go build和go test不会修改go.mod和go.sum文件。能修改这两个文件的命令只有go get和go mod tidy； go get之前的构建和安装go包的行为模式将被废弃。go get将专注于分析依赖，并获取go包/module，更新go.mod/go.sum； go install将恢复自己构建和安装包的“角色”（在go module加入后，go install日益受到冷落，这次翻身了)； go.mod将支持retract指示符，包或module作者可以利用该指示符在自己module的go.mod中标记某些版本撤回(因不安全、不兼容或损坏等原因)，不建议使用。 go.mod中的exclude指示符语义变更：Go 1.16中将忽略exclude指示的module/包依赖；而之前的版本go工具链仅仅是跳过exclude指示的版本，而使用该依赖包/module的下一个版本。 -i build flag废弃； go get的-insecure命令行标志选项作废，可以用GOINSECURE环境变量指示go get是否通过不安全的http去获取包； [...]]]></description>
			<content:encoded><![CDATA[<p>2020年最后一个购物狂欢，<a href="http://image.tonybai.com/img/202011/gopher-tribe-2020-12-12.png">双十二购物节“Gopher部落”知识星球推出双十二优惠！</a>本年度最低折扣仅限今天一天。笔者建立“Gopher部落”旨在建立一个高质量的Go语言技术精品社区，持续不断的高质量技术资料分享，让加入的星友每天都有新收获！欢迎大家加入！</p>
<p><img src="http://image.tonybai.com/img/202011/gopher-tribe-2020-12-12.png" alt="" /></p>
<p>Go 1.16将于2021年2月发布。目前已经进入freeze状态，即不再接受新feature，仅fix bug、编写文档和接受安全更新等。</p>
<p><img src="https://tonybai.com/wp-content/uploads/go-1.16.png" alt="img{512x368}" /></p>
<p>目前Go 1.16的<a href="https://tip.golang.org/doc/go1.16">发布说明</a>尚处于早期草稿阶段，但Go团队成员正在致力于编写发布说明。Go 1.16的完全特性列表说明还得等真正发布前才能得到。如今要了解Go 1.16功能特性都有哪些变化，只能结合现有的release note以及从<a href="https://github.com/golang/go/issues?q=is%3Aissue+milestone%3AGo1.16+is%3Aclosed">Go 1.16里程碑</a>中的issue列表中挖掘。</p>
<p>下面就“挖掘”到的Go 1.16重要功能特性变化做简要的且不完全的前瞻。</p>
<h3>1. 支持Apple Silicon M1芯片</h3>
<p>Apple Silicon M1芯片Macbook的发布让Go团队紧急为Go 1.16增加对M1的支持。如果要跨平台编译，只需设定GOOS=darwin, GOARCH=arm64即可构建出可以在搭载M1芯片的Macbook上运行的Go应用。</p>
<p>同时Go 1.16还增加了对ios/amd64的支持，主要是为了支持在amd64架构上的MacOS上运行ios模拟器。</p>
<h3>2. RISC-V架构支持cgo和-buildmode=pie</h3>
<p>RISC-V架构很可能是未来5-10年挑战ARM的最主要架构，Go语言持续加大对RISC-V架构的支持，在Go 1.16中对linux/riscv64又增加了cgo支持以及-buildmode=pie。不过目前对risc-v仍仅限于linux os。</p>
<h3>3. 有关go module的变化</h3>
<ul>
<li>module-aware模式成为默认状态。如要回到gopath mode，将GO111MODULE设置为auto；</li>
<li>go build和go test不会修改go.mod和go.sum文件。能修改这两个文件的命令只有go get和go mod tidy；</li>
<li>go get之前的构建和安装go包的行为模式将被废弃。go get将专注于分析依赖，并获取go包/module，更新go.mod/go.sum；</li>
<li>go install将恢复自己构建和安装包的“角色”（在go module加入后，go install日益受到冷落，这次翻身了)；</li>
<li><a href="https://github.com/golang/go/issues/24031">go.mod将支持retract指示符</a>，包或module作者可以利用该指示符在自己module的go.mod中标记某些版本撤回(因不安全、不兼容或损坏等原因)，不建议使用。</li>
<li>go.mod中的exclude指示符语义变更：Go 1.16中将忽略exclude指示的module/包依赖；而之前的版本go工具链仅仅是跳过exclude指示的版本，而使用该依赖包/module的下一个版本。</li>
<li>-i build flag废弃；</li>
<li>go get的-insecure命令行标志选项作废，可以用GOINSECURE环境变量指示go get是否通过不安全的http去获取包；</li>
</ul>
<h3>4. 支持在Go二进制文件中嵌入静态文件(文本、图片等)</h3>
<p>Go 1.16新增<strong>go:embed</strong>指示符和embed标准库包，二者一起用于支持在在Go二进制文件中嵌入静态文件。下面是一个在Go应用中嵌入文本文件用于http应答内容的小例子：</p>
<pre><code>// hello.txt
hello, go 1.16

// main.go
package main

import (
         _  "embed"
    "net/http"
)

//go:embed hello.txt
var s string

func main() {
    http.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte(s))
    }))
    http.ListenAndServe(":8080", nil)
}
</code></pre>
<p>上述源码中的<strong>go:embed</strong>指示符的含义是：将hello.txt内容存储在字符串变量s中。我们构建该源码，并验证一下s中存储的是否是hello.txt中的数据：</p>
<pre><code>$ go build -o demo main.go
$ mv hello.txt hello.txt.bak // 将hello.txt改名，我们看看数据是否真的已经嵌入到二进制文件demo中了
$ ./demo

$curl localhost:8080
hello, go 1.16
</code></pre>
<h3>5.GODEBUG环境变量支持跟踪</h3>
<p>当GODEBUG环境变量包含inittrace=1时，Go运行时将会报告各个源代码文件中的init函数的执行时间和内存开辟消耗情况。比如对于上面的程序demo，我们按如下命令执行：</p>
<pre><code># GODEBUG=inittrace=1 ./demo
init internal/bytealg @0.014 ms, 0 ms clock, 0 bytes, 0 allocs
init runtime @0.033 ms, 0.015 ms clock, 0 bytes, 0 allocs
init errors @0.24 ms, 0.003 ms clock, 0 bytes, 0 allocs
init sync @0.47 ms, 0.001 ms clock, 16 bytes, 1 allocs
init io @0.66 ms, 0 ms clock, 144 bytes, 9 allocs
init internal/oserror @0.85 ms, 0 ms clock, 80 bytes, 5 allocs
init syscall @1.0 ms, 0.006 ms clock, 624 bytes, 2 allocs
init time @1.2 ms, 0.013 ms clock, 384 bytes, 8 allocs
init path @1.4 ms, 0.003 ms clock, 16 bytes, 1 allocs
init io/fs @1.6 ms, 0 ms clock, 16 bytes, 1 allocs
init context @2.3 ms, 0.002 ms clock, 128 bytes, 4 allocs
init math @2.5 ms, 0 ms clock, 0 bytes, 0 allocs
init strconv @2.7 ms, 0 ms clock, 32 bytes, 2 allocs
init unicode @2.9 ms, 0.065 ms clock, 23736 bytes, 26 allocs
init bytes @3.2 ms, 0 ms clock, 48 bytes, 3 allocs
init crypto @3.3 ms, 0.001 ms clock, 160 bytes, 1 allocs
init reflect @3.5 ms, 0.002 ms clock, 0 bytes, 0 allocs
init encoding/binary @3.7 ms, 0 ms clock, 16 bytes, 1 allocs
init crypto/cipher @3.8 ms, 0 ms clock, 16 bytes, 1 allocs
init crypto/aes @4.0 ms, 0.003 ms clock, 16 bytes, 1 allocs
init internal/poll @4.1 ms, 0 ms clock, 64 bytes, 4 allocs
init os @4.2 ms, 0.029 ms clock, 544 bytes, 13 allocs
init fmt @4.4 ms, 0.003 ms clock, 32 bytes, 2 allocs
init math/rand @4.5 ms, 0.023 ms clock, 5440 bytes, 3 allocs
init math/big @4.7 ms, 0.002 ms clock, 32 bytes, 2 allocs
init crypto/sha512 @4.8 ms, 0.004 ms clock, 0 bytes, 0 allocs
init encoding/asn1 @5.0 ms, 0.004 ms clock, 224 bytes, 7 allocs
init vendor/golang.org/x/crypto/cryptobyte @5.1 ms, 0 ms clock, 48 bytes, 2 allocs
init crypto/ecdsa @5.3 ms, 0 ms clock, 48 bytes, 3 allocs
init bufio @5.4 ms, 0.003 ms clock, 176 bytes, 11 allocs
init crypto/rand @5.6 ms, 0.001 ms clock, 120 bytes, 4 allocs
init crypto/rsa @5.7 ms, 0.007 ms clock, 648 bytes, 18 allocs
init crypto/sha1 @5.8 ms, 0 ms clock, 0 bytes, 0 allocs
init crypto/sha256 @5.9 ms, 0 ms clock, 0 bytes, 0 allocs
init encoding/base64 @5.9 ms, 0.006 ms clock, 1408 bytes, 4 allocs
init crypto/md5 @6.0 ms, 0 ms clock, 0 bytes, 0 allocs
init encoding/hex @6.1 ms, 0 ms clock, 16 bytes, 1 allocs
init crypto/x509/pkix @6.1 ms, 0.001 ms clock, 624 bytes, 2 allocs
init path/filepath @6.2 ms, 0 ms clock, 16 bytes, 1 allocs
init vendor/golang.org/x/net/dns/dnsmessage @6.3 ms, 0.009 ms clock, 1616 bytes, 27 allocs
init net @6.3 ms, 0.029 ms clock, 2840 bytes, 74 allocs
init crypto/dsa @6.5 ms, 0 ms clock, 16 bytes, 1 allocs
init crypto/x509 @6.5 ms, 0.016 ms clock, 4768 bytes, 15 allocs
init io/ioutil @6.7 ms, 0.002 ms clock, 16 bytes, 1 allocs
init vendor/golang.org/x/sys/cpu @6.7 ms, 0.009 ms clock, 1280 bytes, 1 allocs
init vendor/golang.org/x/crypto/chacha20poly1305 @6.8 ms, 0 ms clock, 16 bytes, 1 allocs
init vendor/golang.org/x/crypto/curve25519 @6.9 ms, 0 ms clock, 0 bytes, 0 allocs
init crypto/tls @7.0 ms, 0.007 ms clock, 1600 bytes, 11 allocs
init log @7.0 ms, 0 ms clock, 80 bytes, 1 allocs
init mime @7.1 ms, 0.008 ms clock, 1232 bytes, 4 allocs
init mime/multipart @7.2 ms, 0.001 ms clock, 192 bytes, 4 allocs
init compress/flate @7.3 ms, 0.012 ms clock, 4240 bytes, 7 allocs
init hash/crc32 @7.4 ms, 0.014 ms clock, 1024 bytes, 1 allocs
init compress/gzip @7.5 ms, 0 ms clock, 32 bytes, 2 allocs
init vendor/golang.org/x/text/transform @7.5 ms, 0 ms clock, 80 bytes, 5 allocs
init vendor/golang.org/x/text/unicode/bidi @7.6 ms, 0.005 ms clock, 272 bytes, 2 allocs
init vendor/golang.org/x/text/secure/bidirule @7.7 ms, 0.008 ms clock, 16 bytes, 1 allocs
init vendor/golang.org/x/text/unicode/norm @7.8 ms, 0.002 ms clock, 0 bytes, 0 allocs
init vendor/golang.org/x/net/idna @7.8 ms, 0 ms clock, 0 bytes, 0 allocs
init vendor/golang.org/x/net/http/httpguts @7.9 ms, 0.002 ms clock, 848 bytes, 3 allocs
init vendor/golang.org/x/net/http2/hpack @7.9 ms, 0.063 ms clock, 22440 bytes, 32 allocs
init net/http/internal @8.1 ms, 0.005 ms clock, 1808 bytes, 3 allocs
init vendor/golang.org/x/net/http/httpproxy @8.2 ms, 0 ms clock, 336 bytes, 2 allocs
init net/http @8.3 ms, 0.026 ms clock, 10280 bytes, 113 allocs
</code></pre>
<p>我们看到各个依赖包中的init函数执行的消耗情况都被输出了出来，根据这些信息，我们可以很容易判断出init函数中可能存在的性能问题或瓶颈。</p>
<h3>6. 链接器进一步优化</h3>
<p>既<a href="https://mp.weixin.qq.com/s/B5onfyP7BPYCh_rMSBtfcQ">Go 1.15</a>实现了go linker的第一阶段优化后，Go 1.16中继续实施了对linker的第二阶段优化。优化后的链接器要平均比Go 1.15的快20%-25%，消耗的内存却减少5%-15%。</p>
<h3>7. struct field的tag中的多个key可以合并写</h3>
<p>如果某个结构体支持多种编码格式的序列化和反序列化，比如：json、bson、xml，那么之前版本需要按如下书写该结构体的字段tag，冗长且重复：</p>
<pre><code>type MyStruct struct {
  Field1 string `json:"field_1,omitempty" bson:"field_1,omitempty" xml:"field_1,omitempty" form:"field_1,omitempty" other:"value"`
}
</code></pre>
<p><a href="https://github.com/golang/go/issues/40281">Go 1.16支持将多个key进行合并</a>，上面的tag可以写成如下形式：</p>
<pre><code>type MyStruct struct {
  Field1 string `json bson xml form:"field_1,omitempty" other:"value"`
}
</code></pre>
<h3>8. 其他改变</h3>
<ul>
<li>新增runtime/metrics包，以替代runtime.ReadMemStats和debug.ReadGCStats输出runtime的各种度量数据，这个包更通用稳定，性能也更好；</li>
<li>新增io/fs包，用于提供只读的操作os的文件树的高级接口；</li>
<li>对Unicode标准的支持从12.0.0升级为13.0.0。 </li>
</ul>
<h3>附录：安装go tip版本的两种方式</h3>
<h4>1) 从源码安装</h4>
<pre><code>$git clone https//github.com/golang/go.git
$cd go/src
$./all.bash
</code></pre>
<h4>2) 使用gotip工具安装</h4>
<pre><code>$go get golang.org/dl/gotip
$gotip download
</code></pre>
<hr />
<p>我的Go技术专栏：“<a href="https://www.imooc.com/read/87">改善Go语⾔编程质量的50个有效实践</a>”上线了，欢迎大家订阅学习！</p>
<p><img src="https://tonybai.com/wp-content/uploads/go-column-pgo-with-qr-and-text.png" alt="img{512x368}" /></p>
<p>我的网课“<a href="https://coding.imooc.com/class/284.html">Kubernetes实战：高可用集群搭建、配置、运维与应用</a>”在慕课网热卖中，欢迎小伙伴们订阅学习！</p>
<p><img src="https://tonybai.com/wp-content/uploads/k8s-practice-with-qr-and-text.png" alt="img{512x368}" /></p>
<p><a href="https://tonybai.com/">我爱发短信</a>：企业级短信平台定制开发专家 https://tonybai.com/<br />
smspush : 可部署在企业内部的定制化短信平台，三网覆盖，不惧大并发接入，可定制扩展； 短信内容你来定，不再受约束, 接口丰富，支持长短信，签名可选。</p>
<p>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>微信赞赏：<br />
<img src="https://tonybai.com/wp-content/uploads/wechat-zanshang-code-512x512.jpg" alt="img{512x368}" /></p>
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。</p>
<p style='text-align:left'>&copy; 2020, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2020/12/12/a-forward-look-to-new-feature-of-go-1-16/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Go，11周年</title>
		<link>https://tonybai.com/2020/11/11/go-opensource-11-years/</link>
		<comments>https://tonybai.com/2020/11/11/go-opensource-11-years/#comments</comments>
		<pubDate>Tue, 10 Nov 2020 23:32:10 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[defer]]></category>
		<category><![CDATA[fuzzing]]></category>
		<category><![CDATA[generics]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[go1.14]]></category>
		<category><![CDATA[go1.15]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[gomodule]]></category>
		<category><![CDATA[GOPATH]]></category>
		<category><![CDATA[GopherCon]]></category>
		<category><![CDATA[gopls]]></category>
		<category><![CDATA[goroutine]]></category>
		<category><![CDATA[linker]]></category>
		<category><![CDATA[Module]]></category>
		<category><![CDATA[pkg.go.dev]]></category>
		<category><![CDATA[protobuf]]></category>
		<category><![CDATA[RussCox]]></category>
		<category><![CDATA[vscode]]></category>
		<category><![CDATA[泛型]]></category>
		<category><![CDATA[链接器]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=2981</guid>
		<description><![CDATA[本文翻译自Go官方博客文章《Eleven Years of Go》，原作者：Russ Cox。 今天，我们一起庆祝Go语言正式开业发布11周年。去年的“Go turning 10”周年庆典聚会似乎已成为久远的回忆。这是艰难的一年，但我们一直保持了Go开发的步伐，并积累了很多亮点。 在去年11月，我们在庆祝Go 10周年后不久就发布和上线了go.dev和pkg.go.dev站点。 今年2月，Go 1.14版本提供了第一个正式的“生产就绪”的go module实现，并进行了许多性能改进，包括更快的defer和真正抢占式的goroutine调度，以减少调度和垃圾收集延迟。 在今年三月初，我们推出了新版protobuf API：google.golang.org/protobuf，大幅改善了对protobuf reflection和自定义消息的支持。 当新冠疫情大流行发生时，我们决定在春季暂停所有公开发布或活动，因为大家都知道所有人的注意力都聚焦在其他地方。但是我们一直在努力，我们的团队中的一个成员加入了Apple/Google发起的“privacy-preserving exposure notifications”项目，以支持全球范围内的联系人追踪工作。5月，该小组启动了用Go编写的 reference backend server。 我们继续改进gopls，这让许多编辑器受益并都启用了高级Go-aware支持。六月份，VSCode Go扩展正式加入Go项目，现在由从事gopls的同一位开发人员维护。 同样在6月，由于Go社区的反馈意见，我们还将pkg.go.dev背后的代码开源，并将其作为Go项目的一部分。 6月下旬，我们 发布了有关Go generics的最新设计草案，以及原型工具和一个支持go generics实验语法的playground。 7月，我们发布并讨论了三个新的有关Go未来演化的设计草案：go:build、文件系统接口和构建时文件嵌入。（我们将在2021年看到所有新特性） 8月，Go 1.15版本发布！该版本以优化和bug修复为主，没有提供太多新功能。其最重要的部分是开始重写链接器，这使它在进行大型项目构建时，平均运行速度提高了20％，平均使用的内存减少了30％。 上个月，我们发起了年度Go用户调查。分析结果后，我们会将结果发布到博客上。 Go社区已经与其他所有人一起适应了“虚拟优先”的原则，今年我们看到了许多虚拟聚会和十多个虚拟Go会议。上周，Go团队在Google Open Source Live中举办了“Go Day”活动。 前进 我们也对Go语言在其第12年即将发生的事情感到非常兴奋。近期，Go团队成员将参加GopherCon 2020并做以下展示和分享。请打开您的日历，做好提醒标记！ 11月11日上午10:00，Robert Griesemer的演讲“Typing [Generic] Go”；在10:30 AM进行Q&#38;A。 11月11日中午12:00，现场播放Go时间播客的实况录像：“What to Expect When You’re NOT Expecting”，该集播客由包括Hana Kim组成的专家调试小组主持。 Michael [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/go-11th-years-old.png" alt="img{512x368}" /></p>
<p>本文翻译自Go官方博客文章<a href="https://blog.golang.org/11years">《Eleven Years of Go》</a>，原作者：<a href="https://swtch.com/~rsc/">Russ Cox</a>。</p>
<p>今天，我们一起庆祝Go语言正式开业发布11周年。去年的<a href="https://tonybai.com/2019/11/09/go-opensource-10-years/">“Go turning 10”</a>周年庆典聚会似乎已成为久远的回忆。这是艰难的一年，但我们一直保持了Go开发的步伐，并积累了很多亮点。</p>
<p><img src="https://tonybai.com/wp-content/uploads/go-11th-years-old-1.jpg" alt="img{512x368}" /></p>
<p>在去年11月，我们在庆祝<a href="https://tonybai.com/2019/11/09/go-opensource-10-years/">Go 10周年</a>后不久就发布和上线了<a href="https://tonybai.com/2019/11/14/what-the-godev-website-bring-to-gophers/">go.dev和pkg.go.dev</a>站点。</p>
<p>今年2月，<a href="https://tonybai.com/2020/03/08/some-changes-in-go-1-14/">Go 1.14版本</a>提供了第一个正式的“生产就绪”的go module实现，并进行了许多性能改进，包括<a href="https://golang.org/design/34481-opencoded-defers">更快的defer</a>和<a href="https://golang.org/design/24543/conservative-inner-frame">真正抢占式的goroutine调度</a>，以减少调度和垃圾收集延迟。</p>
<p>在今年三月初，我们推出了<a href="https://blog.golang.org/protobuf-apiv2">新版protobuf API</a>：<a href="https://pkg.go.dev/google.golang.org/protobuf">google.golang.org/protobuf</a>，大幅改善了对protobuf reflection和自定义消息的支持。</p>
<p>当新冠疫情大流行发生时，我们决定在春季暂停所有公开发布或活动，因为大家都知道所有人的注意力都聚焦在其他地方。但是我们一直在努力，我们的团队中的一个成员加入了Apple/Google发起的<a href="https://www.google.com/covid19/exposurenotifications/">“privacy-preserving exposure notifications”</a>项目，以支持全球范围内的联系人追踪工作。5月，该小组启动了用Go编写的 <a href="https://github.com/google/exposure-notifications-server">reference backend server</a>。</p>
<p>我们继续改进<a href="https://www.youtube.com/watch?v=EFJfdWzBHwE">gopls</a>，这让许多编辑器受益并都启用了<a href="https://github.com/golang/tools/blob/master/gopls/doc/user.md">高级Go-aware支持</a>。六月份，<a href="https://blog.golang.org/vscode-go">VSCode Go扩展正式加入Go项目</a>，现在由从事gopls的同一位开发人员维护。</p>
<p>同样在6月，由于Go社区的反馈意见，我们还<a href="https://blog.golang.org/pkgsite">将pkg.go.dev背后的代码开源</a>，并将其作为Go项目的一部分。</p>
<p>6月下旬，我们 发布了<a href="https://blog.golang.org/generics-next-step">有关Go generics的最新设计草案</a>，以及原型工具和一个<a href="https://go2goplay.golang.org/">支持go generics实验语法的playground</a>。</p>
<p>7月，我们发布并讨论了三个新的有关Go未来演化的设计草案：<a href="https://golang.org/design/draft-gobuild">go:build</a>、<a href="https://golang.org/design/draft-iofs">文件系统接口</a>和<a href="https://golang.org/design/draft-embed">构建时文件嵌入</a>。（我们将在2021年看到所有新特性）</p>
<p>8月，<a href="https://tonybai.com/2020/10/11/some-changes-in-go-1-15/">Go 1.15版本发布</a>！该版本以优化和bug修复为主，没有提供太多新功能。其最重要的部分是开始重写链接器，这使它在进行大型项目构建时，平均运行速度提高了20％，平均使用的内存减少了30％。</p>
<p>上个月，我们发起了<a href="https://blog.golang.org/survey2020">年度Go用户调查</a>。分析结果后，我们会将结果发布到博客上。</p>
<p>Go社区已经与其他所有人一起适应了“虚拟优先”的原则，今年我们看到了许多虚拟聚会和十多个虚拟Go会议。上周，<a href="https://opensourcelive.withgoogle.com/events/go">Go团队在Google Open Source Live中举办了“Go Day”活动</a>。</p>
<h3>前进</h3>
<p>我们也对Go语言在其第12年即将发生的事情感到非常兴奋。近期，Go团队成员将参加<a href="https://www.gophercon.com/">GopherCon 2020</a>并做以下展示和分享。请打开您的日历，做好提醒标记！</p>
<ul>
<li>11月11日上午10:00，Robert Griesemer的演讲“Typing [Generic] Go”；在10:30 AM进行Q&amp;A。</li>
<li>11月11日中午12:00，现场播放Go时间播客的实况录像：“What to Expect When You’re NOT Expecting”，该集播客由包括Hana Kim组成的专家调试小组主持。</li>
<li>Michael Knyszek在11月11日下午1:00发表演讲“Evolving the Go Memory Manager&#8217;s RAM and CPU Efficiency” ；在下午1:50进行Q&amp;A。</li>
<li>Dan Scales在11月11日下午5:10发表演讲“Implementing Faster Defers”； 在下午5:40进行Q&amp;A。</li>
<li>11月12日下午3点，与朱莉·邱（Julie Qiu），丽贝卡·史翠宝（Rebecca Stambler），拉斯·考克斯（Russ Cox），萨默·阿杰曼尼（Sameer Ajmani）和范·里珀（Van Riper）一起的现场问答环节“ Go Team-Ask Me Anything” 。</li>
<li>奥斯汀·克莱门茨（Austin Clements）在11月12日下午4:45发表演讲“Pardon the Interruption: Loop Preemption in Go 1.14” ； 在下午5:15进行Q&amp;A。</li>
<li>乔纳森·阿姆斯特丹（Jonathan Amsterdam）在11月13日下午1:00发表的演讲：“Working with Errors” ； 在下午1:50进行Q&amp;A。</li>
<li>卡门·安多（Carmen Andoh）11月13日下午5:55发表的演讲“Crossing the Chasm for Go: Two Million Users and Growing” 。</li>
</ul>
<h3>Go发布计划</h3>
<p>2021年2月，Go 1.16版本将发布，该版本将包括新的<a href="https://tip.golang.org/pkg/io/fs/">文件系统接口</a>和<a href="https://tip.golang.org/pkg/embed/">构建时文件嵌入</a>。它将完成链接器的重写，从而带来更多的性能改进。它将包括对新的Apple Silicon（GOARCH=arm64）Mac的支持。</p>
<p>2021年8月，Go 1.17版本无疑会带来更多功能和改进，尽管远远不够，确切的细节仍然悬而未决。它将包括一个针对x86-64新的基于寄存器的调用约定（不破坏现有程序集！），这将使程序整体更快。（对其他体系结构的支持将在以后的版本中发布。）新的<strong>//go:build</strong>行肯定会包含一个不错的功能，肯定比当前<strong>// +build</strong>更不容易出错。我们希望明年可以进行Beta测试的另一个备受期待的功能是<a href="https://golang.org/design/draft-fuzzing">对go test命令中的模糊测试(fuzz test)的支持</a>。</p>
<h3>有关Go module</h3>
<p>明年，我们将继续致力于开发对Go module的支持，并将其很好地集成到整个Go生态系统中。Go 1.16将包括我们迄今为止最流畅的Go module体验。我们最近的一项调查的初步结果是，现在有96％的用户已采用Go模块（高于一年前的90％）。</p>
<p>我们还将最终终止对基于GOPATH的开发的支持：使用标准库以外的依赖项的任何程序都将需要一个go.mod。（如果您尚未切换到go module，请参阅<a href="https://golang.org/wiki/GOPATH">GOPATH Wiki页面</a>以获取有关从GOPATH到go module的最后一步的详细信息。）</p>
<p>从一开始，<a href="https://research.swtch.com/vgo-intro">Go module的目标</a>就是“将软件包版本的概念添加到Go开发人员和我们的工具的常用词汇中”，从而为整个Go生态系统中的module和版本提供深度支持。整个生态系统对包版本的广泛理解使得<a href="https://blog.golang.org/modules2019">go module镜像、chechsum数据库和module index</a>成为可能。在明年，我们将看到更多module支持被添加到更多的工具和系统中。例如，我们计划研究新的工具，以帮助模块作者发布新版本（go release），并帮助module使用者摆脱过时的API并完成迁移（新的go fix）。</p>
<p>一个更为有说服力的例子是，<a href="https://github.com/golang/tools/blob/master/gopls/README.md">我们创建了gopls</a>来减少编辑器为支持Go而依赖许多外部工具的情况：将依赖一堆不支持go module的工具转变为只依赖一个支持module的工具。明年，我们将准备让VSCode Go扩展默认使用gopls，以提供出色的、现成的module体验，并将发布gopls 1.0。当然，gopls最大的优势之一是它与编辑器无关：任何支持<a href="https://langserver.org/">语言服务器协议</a>的编辑器都可以使用它。</p>
<p>版本信息的另一个重要用途是跟踪构建中的任何程序包是否具有已知漏洞。明年，我们计划开发一个已知漏洞的数据库以及基于该数据库进行漏洞检查的工具程序。</p>
<p>Go软件包发现站点pkg.go.dev是Go module启用的版本感知系统的另一个示例。我们一直致力于正确实现核心功能和用户体验，包括<a href="https://blog.golang.org/pkgsite-redesign">今天重新设计后的pkg.go.dev的上线</a>。明年，我们将godoc.org统一为pkg.go.dev。我们还将扩展展示每个软件包的版本时间线，显示每个版本的重要更改，已知漏洞等，以实现<a href="https://research.swtch.com/deps">你进行依赖添加决策</a>时所需的所有信息。</p>
<p>我们很高兴看到从GOPATH到Go模块的旅程即将完成，以及Go模块正在启用的所有出色的依赖关系感知工具。</p>
<h3>有关Go generics</h3>
<p>每个人心中的下一个功能特性当然是泛型。如上所述，我们于今年6月发布了<a href="https://blog.golang.org/generics-next-step">有关泛型</a>的<a href="https://blog.golang.org/generics-next-step">最新设计草案</a>。从那时起，我们一直在做细节上的完善，并将注意力转移到了实现可生产版本的细节上。我们将在2021年的整个过程中继续努力，以期在年底之前为人们提供一些试用的目标，也许它是Go 1.18 beta的一部分。</p>
<h3>感谢大家</h3>
<p>Go不仅限于我们这些Google Go团队的成员。我们要感谢与我们一起开发Go项目和工具的贡献者。除此之外，Go之所以成功，是因为所有在Go蓬勃发展的生态系统中工作并为之贡献的人们。Go之外的世界度过了艰难的一年。非常感谢您抽出宝贵的时间加入我们，并帮助Go取得成功。谢谢。我们希望大家都安全，并祝您一切顺利。</p>
<hr />
<p>我的Go技术专栏：“<a href="https://www.imooc.com/read/87">改善Go语⾔编程质量的50个有效实践</a>”上线了，欢迎大家订阅学习！</p>
<p><img src="https://tonybai.com/wp-content/uploads/go-column-pgo-with-qr-and-text.png" alt="img{512x368}" /></p>
<p>我的网课“<a href="https://coding.imooc.com/class/284.html">Kubernetes实战：高可用集群搭建、配置、运维与应用</a>”在慕课网上线了，感谢小伙伴们学习支持！</p>
<p><img src="https://tonybai.com/wp-content/uploads/k8s-practice-with-qr-and-text.png" alt="img{512x368}" /></p>
<p><a href="https://tonybai.com/">我爱发短信</a>：企业级短信平台定制开发专家 https://tonybai.com/<br />
smspush : 可部署在企业内部的定制化短信平台，三网覆盖，不惧大并发接入，可定制扩展； 短信内容你来定，不再受约束, 接口丰富，支持长短信，签名可选。</p>
<p>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>
</ul>
<p>微信赞赏：<br />
<img src="https://tonybai.com/wp-content/uploads/wechat-zanshang-code-512x512.jpg" alt="img{512x368}" /></p>
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。</p>
<p style='text-align:left'>&copy; 2020, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2020/11/11/go-opensource-11-years/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
