<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>第 8 章 泛型 on Go 语言原本</title><link>http://golang.design/under-the-hood/zh-cn/part2lang/ch08generics/</link><description>Recent content in 第 8 章 泛型 on Go 语言原本</description><generator>Hugo</generator><language>zh-cn</language><atom:link href="http://golang.design/under-the-hood/zh-cn/part2lang/ch08generics/index.xml" rel="self" type="application/rss+xml"/><item><title>8.1 泛型设计的演进</title><link>http://golang.design/under-the-hood/zh-cn/part2lang/ch08generics/history/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>http://golang.design/under-the-hood/zh-cn/part2lang/ch08generics/history/</guid><description>&lt;h1 id="81-泛型设计的演进"&gt;8.1 泛型设计的演进&lt;/h1&gt;
&lt;p&gt;泛型是 Go 等待最久、争论最烈、也最能体现其设计哲学的一项特性。从 2009 年开源到 2022 年的
Go 1.18 才落地，这十三年的迟疑与最终的取舍，本身就是一堂语言设计课。这一节回答三个问题：
Go 为何久久不加泛型、最终怎样加的、它在底层又是如何实现的。后者尤其关键，因为 Go 的实现
没有照搬任何一家成例，而是走了一条独特的中间道路，这条道路才是它对那个十三年难题的真正回答。
设计本身一路如何演变（合约、类型集、多轮语法提案），留待 &lt;a href=".././future"&gt;8.4&lt;/a&gt; 细说，本节只取
其骨架。&lt;/p&gt;
&lt;h2 id="811-久拖不决泛型的两难"&gt;8.1.1 久拖不决：泛型的两难&lt;/h2&gt;
&lt;p&gt;缺了泛型，写一段对多种类型行为相同的代码会变成什么样？最直接的办法是为每个类型各抄一份：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div style="background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;"&gt;&lt;tr&gt;&lt;td style="vertical-align:top;padding:0;margin:0;border:0;"&gt;
&lt;pre tabindex="0" style="background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;1
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;2
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;3
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"&gt;
&lt;pre tabindex="0" style="background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#00f"&gt;func&lt;/span&gt; MaxInt(a, b &lt;span style="color:#2b91af"&gt;int&lt;/span&gt;) &lt;span style="color:#2b91af"&gt;int&lt;/span&gt; { &lt;span style="color:#00f"&gt;if&lt;/span&gt; a &amp;gt; b { &lt;span style="color:#00f"&gt;return&lt;/span&gt; a }; &lt;span style="color:#00f"&gt;return&lt;/span&gt; b }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#00f"&gt;func&lt;/span&gt; MaxFloat64(a, b &lt;span style="color:#2b91af"&gt;float64&lt;/span&gt;) &lt;span style="color:#2b91af"&gt;float64&lt;/span&gt; { &lt;span style="color:#00f"&gt;if&lt;/span&gt; a &amp;gt; b { &lt;span style="color:#00f"&gt;return&lt;/span&gt; a }; &lt;span style="color:#00f"&gt;return&lt;/span&gt; b }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#00f"&gt;func&lt;/span&gt; MaxUintptr(a, b &lt;span style="color:#2b91af"&gt;uintptr&lt;/span&gt;) &lt;span style="color:#2b91af"&gt;uintptr&lt;/span&gt; { &lt;span style="color:#00f"&gt;if&lt;/span&gt; a &amp;gt; b { &lt;span style="color:#00f"&gt;return&lt;/span&gt; a }; &lt;span style="color:#00f"&gt;return&lt;/span&gt; b }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#008000"&gt;// ……每多一个类型，就多抄一份逻辑相同的代码&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;逻辑一字不差，只有类型不同。第二条路是用 &lt;code&gt;interface{}&lt;/code&gt; 把类型差异抹掉，让一份代码服务所有类型：&lt;/p&gt;</description></item><item><title>8.2 基于合约的泛型</title><link>http://golang.design/under-the-hood/zh-cn/part2lang/ch08generics/contracts/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>http://golang.design/under-the-hood/zh-cn/part2lang/ch08generics/contracts/</guid><description>&lt;h1 id="82-基于合约的泛型"&gt;8.2 基于合约的泛型&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;本节内容另有一份配套的线上演讲：&lt;a href="https://www.youtube.com/watch?v=E16Y6bI2S08"&gt;YouTube 在线&lt;/a&gt;、
&lt;a href="https://changkun.de/s/go2generics/"&gt;Google Slides 讲稿&lt;/a&gt;。讲稿录于合约提案尚在讨论的 2019 年，
与本节互为印证。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=".././history"&gt;8.1&lt;/a&gt; 用一句话带过了「先有合约、后转向接口即约束」这段演进。这一节把那句话展开：
合约究竟长什么样、它有多强的表达力、又为何最终被放弃。这段被否决的设计不是历史的废料，它解释了
今天 &lt;code&gt;[T Ordered]&lt;/code&gt; 语法的由来，也示范了 Go 设计中一个反复出现的动作,先做出一个表达力很强但
偏复杂的方案，再回头追问「这件事能不能用已有的概念表达」，逼着复杂度去赚取自己存在的资格。&lt;/p&gt;
&lt;p&gt;本节展示的所有 &lt;code&gt;contract&lt;/code&gt; 语法都已废弃，仅出现在 2018 年至 2020 年的 go2draft 草案里，
&lt;strong&gt;任何一个 Go 编译器都从未接受过它们&lt;/strong&gt;。下文凡是合约写法，都按其在草案中的原貌呈现，并明确标注
「已废弃」，以免与今天可用的语法混淆。&lt;/p&gt;
&lt;h2 id="821-约束想解决什么"&gt;8.2.1 约束想解决什么&lt;/h2&gt;
&lt;p&gt;泛型函数迟早要回答一个问题：类型参数 &lt;code&gt;T&lt;/code&gt; 能做哪些操作？写一个求最大值的 &lt;code&gt;Max&lt;/code&gt;，函数体里要用到
比较运算符 &lt;code&gt;&amp;lt;&lt;/code&gt;，可 &lt;code&gt;T&lt;/code&gt; 若被实例化成一个不支持比较的类型（比如某个 struct 或 slice），这段代码就
不成立。于是泛型机制必须提供一种手段，让作者&lt;strong&gt;声明&lt;/strong&gt; &lt;code&gt;T&lt;/code&gt; 必须支持哪些操作，并让编译器据此在
实例化处做检查。这就是「约束」（constraint）。&lt;/p&gt;
&lt;p&gt;约束不是可有可无的修饰。没有它，泛型只剩两条退路：要么像 C++ 早期模板那样&lt;strong&gt;完全不约束&lt;/strong&gt;，类型
错误推迟到实例化时才以一长串难懂的报错暴露出来；要么像 &lt;code&gt;interface{}&lt;/code&gt; 那样&lt;strong&gt;放弃静态信息&lt;/strong&gt;，靠
运行时断言兜底，把类型安全和性能一并丢掉（&lt;a href=".././history"&gt;8.1&lt;/a&gt; 的两难）。约束的价值，正是把
「&lt;code&gt;T&lt;/code&gt; 得能做什么」这件事提前到声明处讲清楚，让错误在调用点就被拦下，也让编译器有依据生成正确的
代码。问题只剩一个：用什么语法把这组要求写出来。&lt;/p&gt;
&lt;h2 id="822-合约一段不会执行的代码"&gt;8.2.2 合约：一段不会执行的代码&lt;/h2&gt;
&lt;p&gt;2018 年 go2draft 给出的第一版答案，是引入一个新关键字 &lt;code&gt;contract&lt;/code&gt;。合约形如一个函数：它带一组
类型参数，函数体里&lt;strong&gt;罗列&lt;/strong&gt;这些类型必须满足的条件。以 &lt;code&gt;Max&lt;/code&gt; 为例，最朴素的设想是直接在体内写出
要用到的运算符（这是草案探讨过的更早一版写法）：&lt;/p&gt;</description></item><item><title>8.3 类型检查技术</title><link>http://golang.design/under-the-hood/zh-cn/part2lang/ch08generics/checker/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>http://golang.design/under-the-hood/zh-cn/part2lang/ch08generics/checker/</guid><description>&lt;h1 id="83-类型检查技术"&gt;8.3 类型检查技术&lt;/h1&gt;
&lt;p&gt;泛型给类型检查器出了远比从前难的题。从前的判断只有一种形状：「这个值是不是这个类型」。
有了类型参数，检查器还得回答三个新问题：一个类型实参是否满足约束、&lt;code&gt;a + b&lt;/code&gt; 这样的运算在
未知类型上是否成立、以及调用 &lt;code&gt;Max(3, 5)&lt;/code&gt; 时那个没写出来的 &lt;code&gt;T&lt;/code&gt; 到底是什么。这三件事分别对应
本节的三个主题：类型集、核心类型、类型推断。它们是 &lt;a href=".././history"&gt;8.1&lt;/a&gt; 那套设计落到编译期的
具体技术，也是 &lt;a href="../../part5toolchain/ch15compile"&gt;15 编译器&lt;/a&gt; 前端的前奏。&lt;/p&gt;
&lt;p&gt;贯穿本节的标尺只有一句：泛型让类型系统的复杂度陡增，而 Go 把这份复杂度尽量关在编译器内部，
不让它溢出到用户面前。读完应当能说清，这份「关」是靠哪几样技术做到的。&lt;/p&gt;
&lt;h2 id="831-类型集约束是一个类型的集合"&gt;8.3.1 类型集：约束是一个类型的集合&lt;/h2&gt;
&lt;p&gt;约束的核心概念是&lt;strong&gt;类型集&lt;/strong&gt;（type set）。一个普通接口描述「有哪些方法」，约束接口则进一步描述
「&lt;strong&gt;哪些类型属于我&lt;/strong&gt;」。把接口从方法集推广为类型集（&lt;a href=".././history"&gt;8.1.2&lt;/a&gt;），是让既有的接口语法
直接承载约束的关键一步。&lt;/p&gt;
&lt;p&gt;类型集由&lt;strong&gt;类型项&lt;/strong&gt;（type term）的并集构成。spec 把类型项的语义定得很干净，编译器
&lt;code&gt;cmd/compile/internal/types2&lt;/code&gt; 里的 &lt;code&gt;term&lt;/code&gt; 结构原样照搬了这套集合论记号：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div style="background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;"&gt;&lt;tr&gt;&lt;td style="vertical-align:top;padding:0;margin:0;border:0;"&gt;
&lt;pre tabindex="0" style="background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 1
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 2
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 3
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 4
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 5
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 6
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 7
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 8
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 9
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;10
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"&gt;
&lt;pre tabindex="0" style="background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#008000"&gt;// types2/typeterm.go：一个类型项描述一个最基本的类型集（速写）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#008000"&gt;//
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#008000"&gt;// ∅: (*term)(nil) == ∅ // 空集&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#008000"&gt;// 𝓤: &amp;amp;term{} == 𝓤 // 全集（所有类型）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#008000"&gt;// T: &amp;amp;term{false, T} == {T} // 恰好是 T 这一个类型&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#008000"&gt;// ~t: &amp;amp;term{true, t} == {t&amp;#39; | under(t&amp;#39;) == t} // 底层类型为 t 的所有类型&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#00f"&gt;type&lt;/span&gt; term &lt;span style="color:#00f"&gt;struct&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; tilde &lt;span style="color:#2b91af"&gt;bool&lt;/span&gt; &lt;span style="color:#008000"&gt;// 仅当 typ != nil 时有效&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; typ Type
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;于是约束里的三种写法各自对应一种类型项：&lt;code&gt;int&lt;/code&gt; 是单点集 $\{\texttt{int}\}$；&lt;code&gt;~int&lt;/code&gt; 是
$\{t \mid \mathrm{under}(t) = \texttt{int}\}$，即所有底层类型为 &lt;code&gt;int&lt;/code&gt; 的类型（&lt;code&gt;~&lt;/code&gt; 取的就是
underlying，故 &lt;code&gt;type Celsius float64&lt;/code&gt; 落在 &lt;code&gt;~float64&lt;/code&gt; 里）；&lt;code&gt;A | B&lt;/code&gt; 是并集 $S_A \cup S_B$。
spec 对 &lt;code&gt;~T&lt;/code&gt; 有两条硬约束：&lt;code&gt;T&lt;/code&gt; 的底层类型必须是它自己（&lt;code&gt;~MyInt&lt;/code&gt; 非法），且 &lt;code&gt;T&lt;/code&gt; 不能是接口
（&lt;code&gt;~error&lt;/code&gt; 非法）。一个约束接口的最终类型集，是它各类型项并集与各方法所蕴含集合的&lt;strong&gt;交集&lt;/strong&gt;：&lt;/p&gt;</description></item><item><title>8.4 泛型的未来</title><link>http://golang.design/under-the-hood/zh-cn/part2lang/ch08generics/future/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>http://golang.design/under-the-hood/zh-cn/part2lang/ch08generics/future/</guid><description>&lt;h1 id="84-泛型的未来"&gt;8.4 泛型的未来&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;本节内容对标 Go 1.26。泛型自 1.18 落地至今已逾四年，本节不再停留在「将会怎样」的猜想，
而是回过头清点：哪些设想兑现了、哪些被有意搁置、以及那个贯穿始终的张力（抽象的代价）今天
走到了哪一步。本节作者早年曾就 Go 2 泛型做过一次公开演讲（&lt;a href="https://www.youtube.com/watch?v=E16Y6bI2S08"&gt;YouTube&lt;/a&gt;、
&lt;a href="https://changkun.de/s/go2generics/"&gt;讲稿&lt;/a&gt;），如今多数预言已可对照现实检验，下文一并交代。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=".././history"&gt;8.1&lt;/a&gt; 讲过泛型从合约到「接口即约束」的十三年演进，&lt;a href=".././checker"&gt;8.3&lt;/a&gt; 讲过类型检查器
如何消化类型参数与约束。落地之后的故事则是另一条线：一项语言特性发布只是起点，它在标准库里
长出哪些惯用法、社区在使用中撞见哪些边界、团队又据此添了什么、按下了什么，这些才决定它最终
的形状。Go 团队对泛型一向自陈谨慎：先发布最小可用的一版，看真实需求浮现，再小步添加。本节
就沿着「已落地、仍缺席、核心张力、演进哲学」四条来盘点这份谨慎换来了什么。&lt;/p&gt;
&lt;h2 id="841-自-118-以来落地了什么"&gt;8.4.1 自 1.18 以来落地了什么&lt;/h2&gt;
&lt;p&gt;泛型在 1.18 只交付了语言层的类型参数与约束。真正让它进入日常代码的，是随后几个版本围绕它
长出的标准库与惯用法。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;slices&lt;/code&gt;、&lt;code&gt;maps&lt;/code&gt;、&lt;code&gt;cmp&lt;/code&gt;：泛型标准库（1.21）。&lt;/strong&gt; 在泛型之前，「对任意切片排序、查找、去重」
要么靠 &lt;code&gt;sort.Slice&lt;/code&gt; 加闭包、要么靠 &lt;code&gt;interface{}&lt;/code&gt; 加反射，两条路都既不安全也不快。1.21 把这些
操作收进三个泛型包。&lt;code&gt;cmp&lt;/code&gt; 给出有序类型的约束与比较原语：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div style="background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;"&gt;&lt;tr&gt;&lt;td style="vertical-align:top;padding:0;margin:0;border:0;"&gt;
&lt;pre tabindex="0" style="background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;1
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;2
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;3
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;4
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;5
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;6
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;7
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;8
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"&gt;
&lt;pre tabindex="0" style="background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#008000"&gt;// cmp 包：把「可比较大小」抽象成一个约束（速写自 src/cmp）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#00f"&gt;type&lt;/span&gt; Ordered &lt;span style="color:#00f"&gt;interface&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ~&lt;span style="color:#2b91af"&gt;int&lt;/span&gt; | ~&lt;span style="color:#2b91af"&gt;int8&lt;/span&gt; | ~&lt;span style="color:#2b91af"&gt;int16&lt;/span&gt; | ~&lt;span style="color:#2b91af"&gt;int32&lt;/span&gt; | ~&lt;span style="color:#2b91af"&gt;int64&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ~&lt;span style="color:#2b91af"&gt;uint&lt;/span&gt; | ~&lt;span style="color:#2b91af"&gt;uint8&lt;/span&gt; | ~&lt;span style="color:#2b91af"&gt;uint16&lt;/span&gt; | ~&lt;span style="color:#2b91af"&gt;uint32&lt;/span&gt; | ~&lt;span style="color:#2b91af"&gt;uint64&lt;/span&gt; | ~&lt;span style="color:#2b91af"&gt;uintptr&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ~&lt;span style="color:#2b91af"&gt;float32&lt;/span&gt; | ~&lt;span style="color:#2b91af"&gt;float64&lt;/span&gt; | ~&lt;span style="color:#2b91af"&gt;string&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#00f"&gt;func&lt;/span&gt; Less[T Ordered](x, y T) &lt;span style="color:#2b91af"&gt;bool&lt;/span&gt; { &lt;span style="color:#00f"&gt;return&lt;/span&gt; (isNaN(x) &amp;amp;&amp;amp; !isNaN(y)) || x &amp;lt; y }
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;注意约束里每一项都带波浪号 &lt;code&gt;~&lt;/code&gt;，意为「底层类型为此」而非「恰为此」，于是用户自定义的
&lt;code&gt;type Celsius float64&lt;/code&gt; 也落在 &lt;code&gt;Ordered&lt;/code&gt; 之内。&lt;code&gt;slices&lt;/code&gt; 与 &lt;code&gt;maps&lt;/code&gt; 则建在这套约束之上，给出
类型安全、无反射的常用操作：&lt;/p&gt;</description></item><item><title>8.5 进一步阅读的参考文献</title><link>http://golang.design/under-the-hood/zh-cn/part2lang/ch08generics/ref/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>http://golang.design/under-the-hood/zh-cn/part2lang/ch08generics/ref/</guid><description>&lt;h1 id="125-进一步阅读的参考文献"&gt;12.5 进一步阅读的参考文献&lt;/h1&gt;
&lt;table class="bib"&gt;
&lt;tr&gt;
&lt;td&gt;[Stroustrup 1994]&lt;/td&gt;&lt;td&gt;Stroustrup, Bjarne. The design and evolution of C++. Pearson Education India, 1994.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[Cox, 2009]&lt;/td&gt;&lt;td&gt;Russ Cox. "The Generic Dilemma." December 3, 2009. &lt;a href="https://research.swtch.com/generic"&gt;https://research.swtch.com/generic&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[Taylor, 2010]&lt;/td&gt;&lt;td&gt;Ian Lance Taylor. "Type Functions." golang/proposals, June 2010. &lt;a href="https://github.com/golang/proposal/blob/master/design/15292/2010-06-type-functions"&gt;https://github.com/golang/proposal/blob/master/design/15292/2010-06-type-functions&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[Taylor, 2011]&lt;/td&gt;&lt;td&gt;Ian Lance Taylor. "Generalized Types."golang/proposals, March 2011. &lt;a href="https://github.com/golang/proposal/blob/master/design/15292/2011-03-gen"&gt;https://github.com/golang/proposal/blob/master/design/15292/2011-03-gen&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[Cox, 2012]&lt;/td&gt;&lt;td&gt;Russ Cox. "Alternatives to Dynamic Code Generation in Go." September 2012. &lt;a href="https://docs.google.com/document/pub?id=1IXHI5Jr9k4zDdmUhcZImH59bOUK0G325J1FY6hdelcM"&gt;https://docs.google.com/document/pub?id=1IXHI5Jr9k4zDdmUhcZImH59bOUK0G325J1FY6hdelcM&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[Taylor, 2013a]&lt;/td&gt;&lt;td&gt;Ian Lance Taylor. "Generalized Types In Go." golang/proposals, October 2013. &lt;a href="https://github.com/golang/proposal/blob/master/design/15292/2013-10-gen"&gt;https://github.com/golang/proposal/blob/master/design/15292/2013-10-gen&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[Taylor, 2013b]&lt;/td&gt;&lt;td&gt;Ian Lance Taylor. "Type Parameters in Go." golang/proposals, December 2013. &lt;a href="https://github.com/golang/proposal/blob/master/design/15292/2013-12-type-params"&gt;https://github.com/golang/proposal/blob/master/design/15292/2013-12-type-params&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[Pike, 2014]&lt;/td&gt;&lt;td&gt;Rob Pike. "Go Generate." January 2014. &lt;a href="http://golang.org/s/go1.4-generate"&gt;http://golang.org/s/go1.4-generate&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[Mills, 2016]&lt;/td&gt;&lt;td&gt;Bryan C. Mills. "Compile-time Functions and First Class Types." golang/proposals, September 2016. &lt;a href="https://github.com/golang/proposal/blob/master/design/15292/2016-09-compile-time-functions"&gt;https://github.com/golang/proposal/blob/master/design/15292/2016-09-compile-time-functions&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[Taylor, 2016]&lt;/td&gt;&lt;td&gt;Ian Lance Taylor. "Go should have generics." golang/proposals, January 2011. &lt;a href="https://go.googlesource.com/proposal/+/b571c3273d2c6988d24a22dd1c529387ff05962a/design/15292-generics"&gt;https://go.googlesource.com/proposal/+/b571c3273d2c6988d24a22dd1c529387ff05962a/design/15292-generics&lt;/a&gt; Updated: April 2016. &lt;a href="https://github.com/golang/proposal/blob/master/design/15292-generics"&gt;https://github.com/golang/proposal/blob/master/design/15292-generics&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[Cox, 2018]&lt;/td&gt;&lt;td&gt;Russ Cox. "Generics — Problem Overview." golang/proposals, August 27, 2018. &lt;a href="https://github.com/golang/proposal/blob/master/design/go2draft-generics-overview"&gt;https://github.com/golang/proposal/blob/master/design/go2draft-generics-overview&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[Taylor and Griesemer, 2018]&lt;/td&gt;&lt;td&gt;Ian Lance Taylor, Robert Griesemer. "Contracts — Draft Design." golang/proposals, August 27, 2018. &lt;a href="https://go.googlesource.com/proposal/+/4a530dae40977758e47b78fae349d8e5f86a6c0a/design/go2draft-contracts"&gt;https://go.googlesource.com/proposal/+/4a530dae40977758e47b78fae349d8e5f86a6c0a/design/go2draft-contracts&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[Taylor and Griesemer, 2019]&lt;/td&gt;&lt;td&gt;Ian Lance Taylor, Robert Griesemer. "Contracts — Draft Design." golang/proposals. July 31, 2019. &lt;a href="https://github.com/golang/proposal/blob/master/design/go2draft-contracts"&gt;https://github.com/golang/proposal/blob/master/design/go2draft-contracts&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[Crowd, 2019a]&lt;/td&gt;&lt;td&gt;Go Community. Summary of Go Generics Discussions. living document. &lt;a href="https://docs.google.com/document/d/1vrAy9gMpMoS3uaVphB32uVXX4pi-HnNjkMEgyAHX4N4/view#"&gt;https://docs.google.com/document/d/1vrAy9gMpMoS3uaVphB32uVXX4pi-HnNjkMEgyAHX4N4/view#&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[Crowd, 2019b]&lt;/td&gt;&lt;td&gt;Go Community. ExperienceReports. living document. &lt;a href="https://github.com/golang/go/wiki/ExperienceReports#generics"&gt;https://github.com/golang/go/wiki/ExperienceReports#generics&lt;/a&gt;, &lt;a href="https://github.com/golang/go/wiki/Go2GenericsFeedback"&gt;https://github.com/golang/go/wiki/Go2GenericsFeedback&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;!-- C++ Standards Committee Papers: &lt;a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/"&gt;http://www.open-std.org/jtc1/sc22/wg21/docs/papers/&lt;/a&gt;
&lt;a href="http://www.stroustrup.com/WG21.html"&gt;http://www.stroustrup.com/WG21.html&lt;/a&gt;
What Happened to C++20 Contracts? &lt;a href="https://www.reddit.com/r/cpp/comments/cmk7ek/what_happened_to_c20_contracts/"&gt;https://www.reddit.com/r/cpp/comments/cmk7ek/what_happened_to_c20_contracts/&lt;/a&gt;
Simple Contracts for C++, &lt;a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4415.pdf"&gt;http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4415.pdf&lt;/a&gt; --&gt;</description></item></channel></rss>