<?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>/zh-cn/part2lang/ch08generics/</link>
    <description>Recent content in 第 8 章 泛型 on Go 语言原本</description>
    <generator>Hugo</generator>
    <language>zh-cn</language>
    <atom:link href="/zh-cn/part2lang/ch08generics/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>8.1 泛型设计的演进</title>
      <link>/zh-cn/part2lang/ch08generics/history/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/zh-cn/part2lang/ch08generics/history/</guid>
      <description>&lt;h1 id=&#34;81-泛型设计的演进&#34;&gt;8.1 泛型设计的演进&lt;/h1&gt;&#xA;&lt;p&gt;泛型是 Go 等待最久、争论最烈、也最能体现其设计哲学的一项特性。从 2009 年开源到 2022 年的&#xA;Go 1.18 才落地，这十三年的迟疑与最终的取舍，本身就是一堂语言设计课。这一节回答三个问题：&#xA;Go 为何久久不加泛型、最终怎样加的、它在底层又是如何实现的。后者尤其关键，因为 Go 的实现&#xA;没有照搬任何一家成例，而是走了一条独特的中间道路，这条道路才是它对那个十三年难题的真正回答。&#xA;设计本身一路如何演变（合约、类型集、多轮语法提案），留待 &lt;a href=&#34;.././future&#34;&gt;8.4&lt;/a&gt; 细说，本节只取&#xA;其骨架。&lt;/p&gt;&#xA;&lt;h2 id=&#34;811-久拖不决泛型的两难&#34;&gt;8.1.1 久拖不决：泛型的两难&lt;/h2&gt;&#xA;&lt;p&gt;缺了泛型，写一段对多种类型行为相同的代码会变成什么样？最直接的办法是为每个类型各抄一份：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;&#xA;&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#xA;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;MaxInt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;         &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;MaxFloat64&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;float64&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;float64&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;MaxUintptr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;uintptr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;uintptr&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// ……每多一个类型，就多抄一份逻辑相同的代码&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&lt;p&gt;逻辑一字不差，只有类型不同。第二条路是用 &lt;code&gt;interface{}&lt;/code&gt; 把类型差异抹掉，让一份代码服务所有类型：&lt;/p&gt;</description>
    </item>
    <item>
      <title>8.2 基于合约的泛型</title>
      <link>/zh-cn/part2lang/ch08generics/contracts/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/zh-cn/part2lang/ch08generics/contracts/</guid>
      <description>&lt;h1 id=&#34;82-基于合约的泛型&#34;&gt;8.2 基于合约的泛型&lt;/h1&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;本节内容另有一份配套的线上演讲：&lt;a href=&#34;https://www.youtube.com/watch?v=E16Y6bI2S08&#34;&gt;YouTube 在线&lt;/a&gt;、&#xA;&lt;a href=&#34;https://changkun.de/s/go2generics/&#34;&gt;Google Slides 讲稿&lt;/a&gt;。讲稿录于合约提案尚在讨论的 2019 年，&#xA;与本节互为印证。&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;&lt;a href=&#34;.././history&#34;&gt;8.1&lt;/a&gt; 用一句话带过了「先有合约、后转向接口即约束」这段演进。这一节把那句话展开：&#xA;合约究竟长什么样、它有多强的表达力、又为何最终被放弃。这段被否决的设计不是历史的废料，它解释了&#xA;今天 &lt;code&gt;[T Ordered]&lt;/code&gt; 语法的由来，也示范了 Go 设计中一个反复出现的动作,先做出一个表达力很强但&#xA;偏复杂的方案，再回头追问「这件事能不能用已有的概念表达」，逼着复杂度去赚取自己存在的资格。&lt;/p&gt;&#xA;&lt;p&gt;本节展示的所有 &lt;code&gt;contract&lt;/code&gt; 语法都已废弃，仅出现在 2018 年至 2020 年的 go2draft 草案里，&#xA;&lt;strong&gt;任何一个 Go 编译器都从未接受过它们&lt;/strong&gt;。下文凡是合约写法，都按其在草案中的原貌呈现，并明确标注&#xA;「已废弃」，以免与今天可用的语法混淆。&lt;/p&gt;&#xA;&lt;h2 id=&#34;821-约束想解决什么&#34;&gt;8.2.1 约束想解决什么&lt;/h2&gt;&#xA;&lt;p&gt;泛型函数迟早要回答一个问题：类型参数 &lt;code&gt;T&lt;/code&gt; 能做哪些操作？写一个求最大值的 &lt;code&gt;Max&lt;/code&gt;，函数体里要用到&#xA;比较运算符 &lt;code&gt;&amp;lt;&lt;/code&gt;，可 &lt;code&gt;T&lt;/code&gt; 若被实例化成一个不支持比较的类型（比如某个 struct 或 slice），这段代码就&#xA;不成立。于是泛型机制必须提供一种手段，让作者&lt;strong&gt;声明&lt;/strong&gt; &lt;code&gt;T&lt;/code&gt; 必须支持哪些操作，并让编译器据此在&#xA;实例化处做检查。这就是「约束」（constraint）。&lt;/p&gt;&#xA;&lt;p&gt;约束不是可有可无的修饰。没有它，泛型只剩两条退路：要么像 C++ 早期模板那样&lt;strong&gt;完全不约束&lt;/strong&gt;，类型&#xA;错误推迟到实例化时才以一长串难懂的报错暴露出来；要么像 &lt;code&gt;interface{}&lt;/code&gt; 那样&lt;strong&gt;放弃静态信息&lt;/strong&gt;，靠&#xA;运行时断言兜底，把类型安全和性能一并丢掉（&lt;a href=&#34;.././history&#34;&gt;8.1&lt;/a&gt; 的两难）。约束的价值，正是把&#xA;「&lt;code&gt;T&lt;/code&gt; 得能做什么」这件事提前到声明处讲清楚，让错误在调用点就被拦下，也让编译器有依据生成正确的&#xA;代码。问题只剩一个：用什么语法把这组要求写出来。&lt;/p&gt;&#xA;&lt;h2 id=&#34;822-合约一段不会执行的代码&#34;&gt;8.2.2 合约：一段不会执行的代码&lt;/h2&gt;&#xA;&lt;p&gt;2018 年 go2draft 给出的第一版答案，是引入一个新关键字 &lt;code&gt;contract&lt;/code&gt;。合约形如一个函数：它带一组&#xA;类型参数，函数体里&lt;strong&gt;罗列&lt;/strong&gt;这些类型必须满足的条件。以 &lt;code&gt;Max&lt;/code&gt; 为例，最朴素的设想是直接在体内写出&#xA;要用到的运算符（这是草案探讨过的更早一版写法）：&lt;/p&gt;</description>
    </item>
    <item>
      <title>8.3 类型检查技术</title>
      <link>/zh-cn/part2lang/ch08generics/checker/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/zh-cn/part2lang/ch08generics/checker/</guid>
      <description>&lt;h1 id=&#34;83-类型检查技术&#34;&gt;8.3 类型检查技术&lt;/h1&gt;&#xA;&lt;p&gt;泛型给类型检查器出了远比从前难的题。从前的判断只有一种形状：「这个值是不是这个类型」。&#xA;有了类型参数，检查器还得回答三个新问题：一个类型实参是否满足约束、&lt;code&gt;a + b&lt;/code&gt; 这样的运算在&#xA;未知类型上是否成立、以及调用 &lt;code&gt;Max(3, 5)&lt;/code&gt; 时那个没写出来的 &lt;code&gt;T&lt;/code&gt; 到底是什么。这三件事分别对应&#xA;本节的三个主题：类型集、核心类型、类型推断。它们是 &lt;a href=&#34;.././history&#34;&gt;8.1&lt;/a&gt; 那套设计落到编译期的&#xA;具体技术，也是 &lt;a href=&#34;/zh-cn/part5toolchain/ch15compile/&#34;&gt;15 编译器&lt;/a&gt; 前端的前奏。&lt;/p&gt;&#xA;&lt;p&gt;贯穿本节的标尺只有一句：泛型让类型系统的复杂度陡增，而 Go 把这份复杂度尽量关在编译器内部，&#xA;不让它溢出到用户面前。读完应当能说清，这份「关」是靠哪几样技术做到的。三个新问题与三样技术&#xA;一一对应，构成本节的骨架：&lt;/p&gt;&#xA;&#xA;&lt;script src=&#34;/mermaid.min.js&#34;&gt;&lt;/script&gt;&#xA;&lt;script src=&#34;/mermaid-init.js&#34;&gt;&lt;/script&gt;&#xA;&#xA;&#xA;&lt;pre class=&#34;mermaid&#34;&gt;flowchart LR&#xA;    Q1[&amp;#34;实参是否满足约束？&amp;#34;] --&amp;gt; T1[&amp;#34;类型集&amp;lt;br/&amp;gt;type set：约束即一个类型的集合&amp;#34;]&#xA;    Q2[&amp;#34;a &amp;#43; b、range s 在未知类型上是否成立？&amp;#34;] --&amp;gt; T2[&amp;#34;核心类型&amp;lt;br/&amp;gt;core / common underlying type&amp;#34;]&#xA;    Q3[&amp;#34;Max(3, 5) 里的 T 是什么？&amp;#34;] --&amp;gt; T3[&amp;#34;类型推断&amp;lt;br/&amp;gt;函数实参合一 &amp;#43; 约束类型推断&amp;#34;]&#xA;    T1 --&amp;gt; IMPL[&amp;#34;types2：编译器内置、懂泛型的类型检查器&amp;#34;]&#xA;    T2 --&amp;gt; IMPL&#xA;    T3 --&amp;gt; IMPL&lt;/pre&gt;&#xA;&lt;h2 id=&#34;831-类型集约束是一个类型的集合&#34;&gt;8.3.1 类型集：约束是一个类型的集合&lt;/h2&gt;&#xA;&lt;p&gt;约束的核心概念是&lt;strong&gt;类型集&lt;/strong&gt;（type set）。一个普通接口描述「有哪些方法」，约束接口则进一步描述&#xA;「&lt;strong&gt;哪些类型属于我&lt;/strong&gt;」。把接口从方法集推广为类型集（&lt;a href=&#34;.././history&#34;&gt;8.1.2&lt;/a&gt;），是让既有的接口语法&#xA;直接承载约束的关键一步。&lt;/p&gt;&#xA;&lt;p&gt;类型集由&lt;strong&gt;类型项&lt;/strong&gt;（type term）的并集构成。spec 把类型项的语义定得很干净，编译器&#xA;&lt;code&gt;cmd/compile/internal/types2&lt;/code&gt; 里的 &lt;code&gt;term&lt;/code&gt; 结构原样照搬了这套集合论记号：&lt;/p&gt;</description>
    </item>
    <item>
      <title>8.4 泛型的未来</title>
      <link>/zh-cn/part2lang/ch08generics/future/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/zh-cn/part2lang/ch08generics/future/</guid>
      <description>&lt;h1 id=&#34;84-泛型的未来&#34;&gt;8.4 泛型的未来&lt;/h1&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;泛型自 1.18 落地至今已逾四年，本节不再停留在「将会怎样」的猜想，&#xA;而是回过头清点：哪些设想兑现了、哪些被有意搁置、以及那个贯穿始终的张力（抽象的代价）今天&#xA;走到了哪一步。本节作者早年曾就 Go 2 泛型做过一次公开演讲（&lt;a href=&#34;https://www.youtube.com/watch?v=E16Y6bI2S08&#34;&gt;YouTube&lt;/a&gt;、&#xA;&lt;a href=&#34;https://changkun.de/s/go2generics/&#34;&gt;讲稿&lt;/a&gt;），如今多数预言已可对照现实检验，下文一并交代。&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;&lt;a href=&#34;.././history&#34;&gt;8.1&lt;/a&gt; 讲过泛型从合约到「接口即约束」的十三年演进，&lt;a href=&#34;.././checker&#34;&gt;8.3&lt;/a&gt; 讲过类型检查器&#xA;如何消化类型参数与约束。落地之后的故事则是另一条线：一项语言特性发布只是起点，它在标准库里&#xA;长出哪些惯用法、社区在使用中撞见哪些边界、团队又据此添了什么、按下了什么，这些才决定它最终&#xA;的形状。Go 团队对泛型一向自陈谨慎：先发布最小可用的一版，看真实需求浮现，再小步添加。本节&#xA;就沿着「已落地、仍缺席、核心张力、演进哲学」四条来盘点这份谨慎换来了什么。&lt;/p&gt;&#xA;&lt;h2 id=&#34;841-自-118-以来落地了什么&#34;&gt;8.4.1 自 1.18 以来落地了什么&lt;/h2&gt;&#xA;&lt;p&gt;泛型在 1.18 只交付了语言层的类型参数与约束。真正让它进入日常代码的，是随后几个版本围绕它&#xA;长出的标准库与惯用法。&lt;/p&gt;&#xA;&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; 在泛型之前，「对任意切片排序、查找、去重」&#xA;要么靠 &lt;code&gt;sort.Slice&lt;/code&gt; 加闭包、要么靠 &lt;code&gt;interface{}&lt;/code&gt; 加反射，两条路都既不安全也不快。1.21 把这些&#xA;操作收进三个泛型包。&lt;code&gt;cmp&lt;/code&gt; 给出有序类型的约束与比较原语：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;&#xA;&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;8&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#xA;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// cmp 包：把「可比较大小」抽象成一个约束（速写自 src/cmp）&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Ordered&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;interface&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;~&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;~&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int8&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;~&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int16&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;~&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int32&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;~&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int64&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;~&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;uint&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;~&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;uint8&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;~&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;uint16&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;~&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;uint32&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;~&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;uint64&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;~&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;uintptr&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;~&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;float32&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;~&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;float64&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;~&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Less&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Ordered&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;](&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;y&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;isNaN&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;isNaN&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;y&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;||&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;y&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&lt;p&gt;注意约束里每一项都带波浪号 &lt;code&gt;~&lt;/code&gt;，意为「底层类型为此」而非「恰为此」，于是用户自定义的&#xA;&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; 则建在这套约束之上，给出&#xA;类型安全、无反射的常用操作：&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
