<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>james (James)</title>
    <link>https://soldev.cn/james</link>
    <description></description>
    <language>en-us</language>
    <item>
      <title>或西或东，或南或北 —— Rust 中的枚举（Enum）</title>
      <description>&lt;p&gt;Rust 是一种强类型语言，意味着每一种“东西”都应归属于某一种确切的类型。类型信息为如何解读内存数据提供了一种标准，明确地规定了其所占内存的宽度与所能表达的范围。在 Rust 中，除指针类型（&lt;code&gt;引用、Trait Object&lt;/code&gt;等）外，其类型的取值范围都是一个有限集合，理论上都是可以被枚举的。比如，u8 的取值范围是&lt;code&gt;[0,255]&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;但 Rust 为什么还要专门设计一个&lt;code&gt;枚举(Enum)&lt;/code&gt;出来呢？其想重点表达的意思是：这种类型其的取值范围相对很少，少到只需要扳扳指头就可以把它枚举完。比如，年循四季，月藏三旬，周迭七日，日替昼夜，人分男女。&lt;/p&gt;

&lt;p&gt;通过枚举机制，用户可以根据业务需求，精确地设计其类型的取值区间。这样做的好处是，能充分享受 Rust 强大的类型系统的红利，把因类型歧义所导致的相关潜在问题提前暴露到编译时。&lt;/p&gt;
&lt;h2 id="枚举的定义与实例化"&gt;枚举的定义与实例化&lt;/h2&gt;
&lt;p&gt;Rust 中的枚举，分为两种。一种跟 C 语言差不多，可以刻画一个枚举所包含的各种状态；另一种，不只可以表示不同的状态，还可以给状态关联上数据。为了方便讨论，我们姑且把前者称为&lt;code&gt;常规型枚举&lt;/code&gt;，后者为&lt;code&gt;关联型枚举&lt;/code&gt;。&lt;/p&gt;
&lt;h3 id="常规型枚举"&gt;常规型枚举&lt;/h3&gt;
&lt;p&gt;要在 Rust 中定义一个枚举类型，只需要用&lt;code&gt;enum&lt;/code&gt;关键字，后面跟着你想要定义类型的名称，然后用对花括号开启一个代码区。在代码区中，分别标记出想要的被称作为&lt;code&gt;变体&lt;/code&gt;的状态即可。&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;//枚举的名称，通常遵循驼峰式命名、大写字母开头&lt;/span&gt;
&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;EnumName&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="n"&gt;State1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;//变体1&lt;/span&gt;
    &lt;span class="n"&gt;State2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;//变体2&lt;/span&gt;
    &lt;span class="n"&gt;State3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;//变体3&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;作为与结构体中&lt;code&gt;字段&lt;/code&gt;的区别，枚举中的状态被称作为&lt;code&gt;变体&lt;/code&gt;。它强调的是，各个状态之间的关系是排它的。即一个具体的枚举实例的值在这些状态中变化，在某一时刻只能是其中某一种状态。&lt;/p&gt;

&lt;p&gt;来看一个例子，如果要定义表示性别的枚举 &lt;code&gt;Gender&lt;/code&gt;，它的可能取值范围是：&lt;code&gt;雄性&lt;/code&gt; 与 &lt;code&gt;雌性&lt;/code&gt;。&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="cd"&gt;/// 表示性别的枚举&lt;/span&gt;
&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;Gender&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Male&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;// 雄性&lt;/span&gt;
    &lt;span class="n"&gt;Female&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// 雌性&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;现在已经定义好了 &lt;code&gt;Gender&lt;/code&gt;枚举类型，可以创建该类型的两个不同变体的实例：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;male&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Gender&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Male&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;female&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Gender&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Female&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;注意上面的&lt;code&gt;::&lt;/code&gt;连接符号，它表示是某个枚举类型下面的某个的变体。我们采用&lt;code&gt;EnumeName::StateName&lt;/code&gt;方式，唯一确定一个枚举值。这与老式的 C 语言中的枚值是不一样的。其好处是，不容易出现名称上的冲突，同一个变体的名字可以在别的枚举中使用。&lt;/p&gt;

&lt;p&gt;由于&lt;code&gt;Gender::Male&lt;/code&gt; 和 &lt;code&gt;Gender::Female&lt;/code&gt;都是 &lt;code&gt;Gender&lt;/code&gt;类型的值，那么就可以把它们传递到一个接收&lt;code&gt;Gender&lt;/code&gt;类型作为参数的函数中了。比如：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fn perform_some_task_with_gender(gender: Gender, ...some_rest_args) {}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;还记得我们之前为老刘的马定义的结构体吗？当时，我们把马的性别定义为&lt;code&gt;u8&lt;/code&gt;，并约定：&lt;code&gt;0&lt;/code&gt;表示母马、&lt;code&gt;1&lt;/code&gt;表示公马。那么实例化时，如果传入数字&lt;code&gt;100&lt;/code&gt;，该如何应对呢？在根据性别作不同处理的代码逻辑中，就可能会出现业务层面的&lt;code&gt;Bug&lt;/code&gt;！如果使用我们定义的&lt;code&gt;Gender&lt;/code&gt;作为类型的话，就不会出现上述的问题，因为其取值被限制了在合法的范围内。下面是修改后的代码：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Horse&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="c1"&gt;// --snip--&lt;/span&gt;
    &lt;span class="n"&gt;gender&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Gender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// 马的性别，被限制在合法的范围内。&lt;/span&gt;
    &lt;span class="c1"&gt;// --snip--&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="关联型枚举"&gt;关联型枚举&lt;/h3&gt;
&lt;p&gt;与传统的 C 语言不同，Rust 支持在枚举中的变体上关联想要的数据。其大致语法如下：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;EnumName&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;State1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nf"&gt;State2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AssociatedType1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nf"&gt;State3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AssociatedType2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;对所关联的数据类型没有要求，可以是除正在定义的枚举本身类型之外的任何类型。如果想关联正在定义的枚举本身类型，可以改为相应的指针类型。另外，在一个枚举类型中，&lt;code&gt;没有关联数据的&lt;/code&gt;变体 可以与&lt;code&gt;关联数据的变体&lt;/code&gt;并存。各个变体所关联的数据类型也可以不一样。&lt;/p&gt;

&lt;p&gt;我们乘坐时间快车，穿越至大明洪武年间，来到了去圣人山冬瓜岭的路上。刚越岭，见一人立于松下，时远眺东南之来路。见我等来人，笑迎而至。拱手道：“昨夜梦中见天开，知有贵客至，今早候于此矣。”此人正是故人一明先生。入其舍，鸡鸣犬吠，堂中新筑土屋，梁柱粗实，院中竹篱环绕，鸡鸭成群，颇具田园之乐。一明端茶以待，笑言：“吾弃文而耕田，六载有余，方知躬耕自乐，胜于枯坐书斋也。”茶过三盏，杀鸡烹羊，炖汤煮菜。红泥火炉笑欢颜，几碗浊酒话天边。与君共此时，一杯何以酣？&lt;/p&gt;

&lt;p&gt;看得出来，一明先生通过几年辛勤劳作，积累了不少的财富。下面是老刘脱密后的资产清单：&lt;/p&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="err"&gt;妻：只能有一个，这是明朝哦，女人也是男人的财产&lt;/span&gt;
&lt;span class="err"&gt;妾：可以有多个&lt;/span&gt;
&lt;span class="err"&gt;房屋：以栋数为单位计数&lt;/span&gt;
&lt;span class="err"&gt;家畜：牛、马、羊等&lt;/span&gt;
&lt;span class="err"&gt;粮食：玉米、稻谷、黄豆等&lt;/span&gt;
&lt;span class="err"&gt;蔬菜：因白菜等无法保存，通常直接长在地里，以种植面积计算&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;为了记录老刘手头的资产，我们怎样设计相关的数据类型呢？一个常见的思路，先设计一个用来表示资产种类的枚举，再设计一个包含有 2 个字段的结构体，一个字段的类型为资产种类，别一个为对应的数量。但是，这里存在一些棘手的问题，有的种类不需要数量信息（因为它只有一个，比如妻子），有的种类又存在多级嵌套（比如牛和玉米，它们还存在上一级类型）。当考虑到这些问题之后，其复杂性就增加了不少。面对这种场景，正是 Rust 中枚举发挥威力的时候，因为它可以关联数据。现在，我们尝试设计一下吧。&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="cd"&gt;/// 资产枚举&lt;/span&gt;
&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;Asset&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Wife&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                           &lt;span class="c1"&gt;// 正妻，只能有一个，故无需关联数据&lt;/span&gt;
    &lt;span class="nf"&gt;Concubine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;                  &lt;span class="c1"&gt;// 妾，可以有多个&lt;/span&gt;
    &lt;span class="nf"&gt;House&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;                      &lt;span class="c1"&gt;// 房子，以栋计算&lt;/span&gt;
    &lt;span class="nf"&gt;Livestock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LivestockType&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;       &lt;span class="c1"&gt;// 家畜，关联家畜种类&lt;/span&gt;
    &lt;span class="nf"&gt;Grain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;             &lt;span class="c1"&gt;// 粮食作物，关联作物种类和存量（单位：斤）&lt;/span&gt;
    &lt;span class="c1"&gt;// 因为蔬菜不便储存，所以以种植面积估算&lt;/span&gt;
    &lt;span class="n"&gt;Vegetable&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;                     &lt;span class="c1"&gt;// 蔬菜，使用结构体样式，关联蔬菜种类和种植面积&lt;/span&gt;
        &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;               &lt;span class="c1"&gt;// 蔬菜名称，假设种类繁多不便统一，使用String&lt;/span&gt;
        &lt;span class="n"&gt;area&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;f32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                  &lt;span class="c1"&gt;// 种植面积（单位：亩）&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cd"&gt;/// 家畜种类的枚举&lt;/span&gt;
&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;LivestockType&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Cattle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="c1"&gt;// 牛&lt;/span&gt;
    &lt;span class="n"&gt;Horse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="c1"&gt;// 马&lt;/span&gt;
    &lt;span class="n"&gt;Goat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="c1"&gt;// 山羊&lt;/span&gt;
    &lt;span class="n"&gt;Pig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;       &lt;span class="c1"&gt;// 猪&lt;/span&gt;
    &lt;span class="n"&gt;Dog&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;       &lt;span class="c1"&gt;// 狗&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;对于&lt;code&gt;Asset&lt;/code&gt;枚举，其中的&lt;code&gt;Wife&lt;/code&gt;没有关联数据，&lt;code&gt;Concubine&lt;/code&gt;和&lt;code&gt;House&lt;/code&gt;关联的是一个单一类型&lt;code&gt;u8&lt;/code&gt;，&lt;code&gt;Livestock&lt;/code&gt;关联的是另一个枚举类型&lt;code&gt;LivestockType&lt;/code&gt;，&lt;code&gt;Grain&lt;/code&gt;关联的是元组类型&lt;code&gt;(String, u32)&lt;/code&gt;。&lt;code&gt;Vegetable&lt;/code&gt;比较特殊，它关联的是一种称作为匿名结构体的类型：&lt;code&gt;{name: String, area: f32,}&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;定义好了这些类型后，就可以用它们来表示具体的资产呢了。&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 300斤稻谷&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;paddy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Asset&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Grain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"稻谷"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// 1.5亩白菜&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;cabbages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Asset&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Vegetable&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"白菜"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="n"&gt;area&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1.5&lt;/span&gt;&lt;span class="p"&gt;,};&lt;/span&gt;

&lt;span class="c1"&gt;// 牛&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;cattle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Asset&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Livestock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;LivestockType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Cattle&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;细心的同学应该已经发现，上面的类型设计是有缺陷的，不是吗？现在只能标识家畜种类，不能记录其数量。所以，我们要对上面的&lt;code&gt;LivestockType&lt;/code&gt;做一些调整，其目的是用于资产的统计，所以需要另外再设计一个类型。下面是重新设计后的代码：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="cd"&gt;/// 用于资产统计的家畜种类的枚举&lt;/span&gt;
&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;LivestockAssetType&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;Cattle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Gender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;    &lt;span class="c1"&gt;// 牛&lt;/span&gt;
    &lt;span class="nf"&gt;Horse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Gender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;     &lt;span class="c1"&gt;// 马&lt;/span&gt;
    &lt;span class="nf"&gt;Goat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Gender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;u16&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;     &lt;span class="c1"&gt;// 山羊&lt;/span&gt;
    &lt;span class="nf"&gt;Pig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;               &lt;span class="c1"&gt;// 猪 作为资产，在市面上不需要区分公母&lt;/span&gt;
    &lt;span class="nf"&gt;Dog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;               &lt;span class="c1"&gt;// 狗 作为资产，在市面上不需要区分公母&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cd"&gt;/// 资产枚举&lt;/span&gt;
&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;Asset&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// --snip--&lt;/span&gt;
    &lt;span class="nf"&gt;Livestock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LivestockAssetType&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;  &lt;span class="c1"&gt;// 家畜，关联家畜种类&lt;/span&gt;
    &lt;span class="c1"&gt;// --snip--&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;现在，我们就可以记录家畜资产了：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 3头公牛&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;cattle_asset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Asset&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Livestock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;LivestockAssetType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Cattle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Male&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="c1"&gt;// 2匹公马&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;horse_asset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Asset&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Livestock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;LivestockAssetType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Horse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Male&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;完美！是不是很方便呢？&lt;/p&gt;

&lt;p&gt;枚举还有一个好处就是，通过把不同的类型包装在同一个枚举类型中，使其成为同一的类型，方便与外界交互。比如下面的记账函数，它根据家畜市场价格，把各种资产货币化后记入相应的会计科目中。虽然资产种类繁多，但由于我们通过枚举把它们统一化了，所以不管是牛还是马，都可以被视作同一物传入到函数的参数中。&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;record_assets2accounts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;livestock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;LivestockAssetType&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="枚举的关联函数与方法"&gt;枚举的关联函数与方法&lt;/h2&gt;
&lt;p&gt;枚举跟结构体一样，也可以有关联函数与关联方法。其使用要求与结构体是一样的，具体情况请参照结构体中相关内容，在此就不再赘述了。&lt;/p&gt;
&lt;h2 id="常用枚举类型"&gt;常用枚举类型&lt;/h2&gt;&lt;h3 id="Option"&gt;Option&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;Option&lt;/code&gt; 是 Rust 标准库中一种枚举，它用来表明一个类型可以有空值的概念，即要么有效值，要么为空。其设计目的是为了解决传统编程语言中&lt;code&gt;空值(null)&lt;/code&gt;引发的问题，通过类型系统提供一种安全、显式的方式来表示一个值要么存在（Some），要么缺失（None）。在许多语言中，空值通常由 null 或类似的概念表示，这种设计虽然简单，但存在一些显著问题：首先，空值的存在是隐式的，程序员无法在类型层面确定某个值是否可能为 null；其次，访问空值通常会导致运行时错误，这种错误往往不容易在编译阶段发现。&lt;/p&gt;

&lt;p&gt;Option 的定义如下：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// 表示值存在，包含一个类型T的值&lt;/span&gt;
    &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="c1"&gt;// 表示空值&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;Some(T)&lt;/code&gt;表示有一个类型&lt;code&gt;T&lt;/code&gt; 的值，&lt;code&gt;T&lt;/code&gt;是一个泛型类型（我们还没有学习泛型，你现在可以简单地把它当成是一个类型的占位符），这意味着 &lt;code&gt;Option&lt;/code&gt; 枚举的&lt;code&gt;Some&lt;/code&gt;变体可以包含任意类型的数据。&lt;code&gt;None&lt;/code&gt;表示没有值，等同于别的语言中&lt;code&gt;空值(null)&lt;/code&gt;的概念。看几个例子：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;some_number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// some_number的类型为 Option&amp;lt;i32&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;some_string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"a string"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;//some_string的类型为 Option&amp;lt;&amp;amp;str&amp;gt;&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;absent_number&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;i32&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;//必须加上完整的类型信息，因为无法推定Option&amp;lt;T&amp;gt;中的T&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果使用 &lt;code&gt;None&lt;/code&gt; 而不是 &lt;code&gt;Some&lt;/code&gt;，需要告诉 Rust &lt;code&gt;Option&amp;lt;T&amp;gt;&lt;/code&gt; 是什么类型的，因为编译器只通过 &lt;code&gt;None&lt;/code&gt; 值无法推断出 &lt;code&gt;Some&lt;/code&gt; 成员保存的值的类型。每一个&lt;code&gt;Option&amp;lt;T&amp;gt;&lt;/code&gt;中，都可能会有&lt;code&gt;None&lt;/code&gt;，无法分辨它到底属于哪一个类型。&lt;/p&gt;

&lt;p&gt;当为&lt;code&gt;Some&lt;/code&gt;值时，我们就知道存在一个值。当为&lt;code&gt;None&lt;/code&gt;值时，在某种意义上，它跟空值具有相同的意义：并没有一个有效的值。那么，&lt;code&gt;Option&amp;lt;T&amp;gt;&lt;/code&gt;为什么就比空值要好呢？因为&lt;code&gt;Option&amp;lt;T&amp;gt;&lt;/code&gt; 和 &lt;code&gt;T&lt;/code&gt;是不同的类型，编译器不允许像一个肯定有效的值那样使用&lt;code&gt;Option&amp;lt;T&amp;gt;&lt;/code&gt;。例如，这段代码不能编译，因为它尝试将&lt;code&gt;Option&amp;lt;i8&amp;gt;&lt;/code&gt; 与&lt;code&gt;i8&lt;/code&gt; 相加：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;i8&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;i8&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们忽略编译器的具体错误信息，尝试分析一下它未能通过编译的原因。给一个变量指定为&lt;code&gt;Option&lt;/code&gt;的时候，我们是想表达它可能不存在值（空值），那么在使用的时候，我们肯定需要注意它的非空与空的两种状态，对其分别做出相应的处理，特别是当它是空的情况。也就是说，Rust 通过提供&lt;code&gt;Option&amp;lt;T&amp;gt;&lt;/code&gt;类型，让它与&lt;code&gt;T&lt;/code&gt;是两个不同类型，这样会由类型系统保证程序员不会忽略对空值存在。如果采用传统语言中的&lt;code&gt;空值(null&lt;/code&gt;机制，由于空值隐含在类型中（一体两面），一不小心就会忽略对空值的特殊处理。&lt;/p&gt;

&lt;p&gt;类型系统阻止&lt;code&gt;Option&amp;lt;i8&amp;gt;&lt;/code&gt; 与&lt;code&gt;i8&lt;/code&gt;相加，因为它们不是同一种类型，或者说两个类型之间没有实现相加法操作。只有在&lt;code&gt;Option&amp;lt;i8&amp;gt;&lt;/code&gt;存在有效值的时候，把有效值&lt;code&gt;i8&lt;/code&gt;抽取出来后才能相加。代码如下：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;i8&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;i8&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;//如果是Some(T),获取内容T;如果是None直接panic&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;y_i8&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; 

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;y_i8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;从&lt;code&gt;Option&lt;/code&gt;中获取有效值，可以通过代码所示的&lt;code&gt;unwrap&lt;/code&gt;（如果能确定它有值），也可以采取后文介绍的&lt;code&gt;match&lt;/code&gt;与&lt;code&gt;if let&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Option&lt;/code&gt;是一个非常强大的工具，标准库和实际项目中都有它的身影。它提供了大量实用的&lt;code&gt;API&lt;/code&gt;，我们不可能介绍它的所有细节。其具体情况，请参照相关文档。&lt;/p&gt;
&lt;h3 id="Result"&gt;Result&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;Result&lt;/code&gt;是标准库给我提供的另一个很重要的枚举类型，其主要作用是用于错误处理。关于错误处理的内容，后续会有专文介绍。在这里，只是从枚举类型的角度，对它作一个简单的介绍。其定义如下：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;E&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;  &lt;span class="c1"&gt;// 表示操作成功，包含成功的值 T&lt;/span&gt;
    &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;E&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// 表示操作失败，包含错误信息 E&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;Result&lt;/code&gt;是一个枚举，包含两个变体：&lt;code&gt;Ok(T)&lt;/code&gt; 和 &lt;code&gt;Err(E)&lt;/code&gt;。&lt;code&gt;Ok&lt;/code&gt; 表示操作成功，并包含成功的值 &lt;code&gt;T&lt;/code&gt;；&lt;code&gt;Err&lt;/code&gt; 表示操作失败，并携带错误信息 &lt;code&gt;E&lt;/code&gt;。这种双重状态的设计，使得程序员在使用时必须显式地处理错误情况，从根本上避免了传统异常机制可能带来的隐蔽性问题。&lt;/p&gt;
&lt;h2 id="操作枚举的常用方法"&gt;操作枚举的常用方法&lt;/h2&gt;&lt;h3 id="match 运算符"&gt;match 运算符&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;match&lt;/code&gt; 是一种控制流运算符，在功能上类似于&lt;code&gt;if else&lt;/code&gt; 和有些语言（比如 C 语言）中的&lt;code&gt;swith&lt;/code&gt;语句。但&lt;code&gt;match&lt;/code&gt;要比它们强大，因为它在做判断时支持模式匹配（模式匹配会另开专文讨论）。可以把模式匹配想象成某种硬币分类器：硬币滑入有着不同大小孔洞的轨道，每一个硬币都会掉入符合它大小的孔洞。同样地，值也会通过 match 的每一个模式，并且在遇到第一个“符合”的模式时，值会进入相关联的代码块并在执行中被使用。&lt;/p&gt;

&lt;p&gt;还是拿我们之前提及过的家畜记帐函数为例，看看如何使用 match 实现其记帐逻辑。&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;record_assets2accounts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;livestock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;LivestockAssetType&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// --snip--&lt;/span&gt;
    &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;livestock&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nn"&gt;LivestockAssetType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Cattle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/*在花括号内，处理牛相关的逻辑*/&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="nn"&gt;LivestockAssetType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Horse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt;    
                        &lt;span class="nn"&gt;Horse&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;record_2_accounts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="c1"&gt;//一条语句不用加花括号&lt;/span&gt;
        &lt;span class="nn"&gt;LivestockAssetType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Goat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/*在花括号内，处理羊相关的逻辑*/&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="nn"&gt;LivestockAssetType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Pig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cout&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/*在花括号内，处理猪相关的逻辑*/&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* _ 表示除了上面处理的状态外的其他所有状态，本例中只包括狗*/&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// --snip--&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上面的示例代码，根据 livestock 的各种状态，分门别类地做了相应的处理。代码中各个状态关联的数据使用了模式匹配，比如：&lt;code&gt;Pig(count)&lt;/code&gt;，由于表示猪的变体定义是&lt;code&gt;Pig(u8)&lt;/code&gt;，所以&lt;code&gt;count&lt;/code&gt;会匹配到猪所关联的数量；&lt;code&gt;Cattle(gender, count)&lt;/code&gt;，由于表示牛的变体定义是&lt;code&gt;Cattle(Gender, u8)&lt;/code&gt;，所以&lt;code&gt;gender、count&lt;/code&gt;会分别匹配到牛所关联的性别与数量。还可以使用&lt;code&gt;_&lt;/code&gt;，忽略相应的关联的数据，比如&lt;code&gt;Goat(_, count)&lt;/code&gt;忽略羊的性别，这可能是公羊与母羊在市面上的价格是一样。&lt;/p&gt;

&lt;p&gt;现在做一个简单的总结：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;match&lt;/code&gt;会对一个值（表达式计算后会返回一个值），在所对应类型的所有取值范内进行判定，不能遗漏任何一种情形。&lt;/li&gt;
&lt;li&gt;如果满足判定条件，处理 &lt;code&gt;=&amp;gt;&lt;/code&gt;后对应的代码段。判定时用到了模式匹配，它可以绑定值，以便在对应的处理代码中使用。也可以用&lt;code&gt;-&lt;/code&gt;，忽略相应位置所对应的绑定的值。&lt;/li&gt;
&lt;li&gt;可以用&lt;code&gt;-&lt;/code&gt;表示除已经列出的状态之外的情况，这种情形也必须要有对应的处理。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;match&lt;/code&gt; 代码块在整体上作为一个表达式会返回值，这就意味着各个分支处理代码（&lt;code&gt;=&amp;gt; {...}&lt;/code&gt;）返回值的类型必须一致。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;关于上述的最后一点，举个例子解释一下。比如：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;handle_with_gender&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gender&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Gender&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// --snip--&lt;/span&gt;
    &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;gender&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Male&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"malle"&lt;/span&gt;&lt;span class="p"&gt;);},&lt;/span&gt; &lt;span class="c1"&gt;//此处的返回类型是 `()`&lt;/span&gt;
        &lt;span class="n"&gt;Female&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// 此处的返回类型是 `i32`&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// --snip--&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;编译上面代码会出现：&lt;code&gt;match arms have incompatible types&lt;/code&gt;的错误，这是显然的，因为&lt;code&gt;match&lt;/code&gt; 作为一个表达式，它应该有自己确切的类型，而出现两种不一样的类型必然是不允许的。如果还不明白，把它赋给一个变量就明白了。&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 当编译器为`ret`推定类型时，就陷入了两难之地。ret到底应该是哪种类型呢？&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;ret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;gender&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="if let 控制流"&gt;if let 控制流&lt;/h3&gt;
&lt;p&gt;在搞明白了&lt;code&gt;match&lt;/code&gt;后，再来讨论&lt;code&gt;if let&lt;/code&gt;就相当简单了。我们知道，使用&lt;code&gt;match&lt;/code&gt;时，必须要对每一种情况都进行处理，漏掉任何一个都不能通过编译。试想这样一个情况，假如一个枚举有 10 个状态，我们只关心其中的某一个状态，其他情况全部忽略。如果用&lt;code&gt;match&lt;/code&gt;实现的话，代码逻辑大致如下：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;some_enum_instance&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Sate_Cared&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样是不是很麻烦？每一次都必须写上&lt;code&gt;_&lt;/code&gt;来处理其他情况。在这种场景下，就是&lt;code&gt;if let&lt;/code&gt;发挥威力的时候，可以少写很多代码。与上面样版代码等价的用&lt;code&gt;if let&lt;/code&gt;代码如下：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;some_cared_enum_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;some_enum_instance&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;下面我们分别用&lt;code&gt;match&lt;/code&gt;与&lt;code&gt;if let&lt;/code&gt;举几个具体例子，两者都完成一样的功能。&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;livestock_asset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;LivestockAssetType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Dog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;livestock_asset&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;LivestockAssetType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Dog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"There have {} dogs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;//对应的`if let`代码：&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nn"&gt;LivestockAssetType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Dog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;livestock_asset&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"There have {} dogs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;if let&lt;/code&gt; 还可以跟上&lt;code&gt;else&lt;/code&gt;，用于处理其他的情形，比如：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;some_u8_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0u8&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;some_u8_value&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;//对应的`if let`代码：&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;some_u8_value&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="枚举的内存布局"&gt;枚举的内存布局&lt;/h2&gt;
&lt;p&gt;在 Rust 中，枚举的内存布局由两部分组成：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;标签（Tag）：标识当前枚举属于哪一个变体。&lt;/li&gt;
&lt;li&gt;数据（Data）：存储与变体相关联的数据，取关联数据中占用空间最长的变体。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;枚举的内存分配方式取决于其变体及其关联数据的特性。Rust 会采用一种最优的方式，以确保枚举占用最小的内存空间。&lt;/p&gt;
&lt;h3 id="常规枚举的内存布局"&gt;常规枚举的内存布局&lt;/h3&gt;
&lt;p&gt;对于不携带关联数据的枚举（称为 C 风格枚举），其内存布局与整数非常相似。例如：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;Direction&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;North&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;East&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;South&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;West&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Direction 枚举包含四个变体，每个变体的值在底层会被编译为整数。具体的布局可以类比为：&lt;/p&gt;
&lt;table class="table table-bordered table-striped"&gt;
&lt;tr&gt;
&lt;th style="text-align:center;"&gt;枚举变体&lt;/th&gt;
&lt;th style="text-align:center;"&gt;对应整数值&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:center;"&gt;&lt;code&gt;North&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align:center;"&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:center;"&gt;&lt;code&gt;East&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align:center;"&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:center;"&gt;&lt;code&gt;South&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align:center;"&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:center;"&gt;&lt;code&gt;West&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align:center;"&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;此时，&lt;code&gt;Direction&lt;/code&gt; 的大小为一个 &lt;code&gt;u8&lt;/code&gt;，因为取二位就足够了（2 的 2 次方刚好能表示四个变体），但长度是以字节为单位，最少也要一个字节 (&lt;code&gt;u8&lt;/code&gt;）。&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;direction1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Direction&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;East&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Size of Direction: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;mem&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;size_of_val&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;direction1&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// 输出 1&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;所对应的内存布局如下图：&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/james/55be9d53-57a4-4b30-952c-4a144bee7502.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h3 id="关联数据枚举的内存布局"&gt;关联数据枚举的内存布局&lt;/h3&gt;
&lt;p&gt;如果枚举的变体携带数据，Rust 会为变体关联数据分配内存，并在枚举中增加一个标签来指示当前变体。例如：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nf"&gt;Float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;f64&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在这个例子中，Value 的内存布局需要同时容纳：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;一个变体标签（1 bit 表示 &lt;code&gt;Int&lt;/code&gt; 或 &lt;code&gt;Float&lt;/code&gt;）。&lt;/li&gt;
&lt;li&gt;两种数据中的最大值（&lt;code&gt;f64&lt;/code&gt; 占 8 字节）。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;因此，&lt;code&gt;Value&lt;/code&gt; 的内存大小为 16 字节。这是因为：最大的变体是&lt;code&gt;f64&lt;/code&gt;类型，需要占用 8 个字节，另外还有用于记录变体的标签位占 1 个字节。这两者跟结构体字段一样是组合关系，所以需要参照结构体布局方案，即需要考虑内存对齐。对齐值为两者的最小公倍数，所以对 1 个字节的标签位扩展到 8 个字节。&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;2.0_f64&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Size of Direction: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;mem&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;size_of_val&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// 会输出 16&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;所对应的内存布局如下图：
&lt;img src="/uploads/photo/james/c5195784-3ac9-408b-8109-11a155d8a848.jpg!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h3 id="空值优化布局"&gt;空值优化布局&lt;/h3&gt;
&lt;p&gt;Rust 利用指针的特殊值（如 NULL）优化内存布局，使一些枚举类型不需要额外的空间。例如：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;对于 &lt;code&gt;Option&amp;lt;&amp;amp;T&amp;gt;&lt;/code&gt; 类型，Rust 会利用指针值本身的空值语义进行优化：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;None 表示为 NULL。&lt;/li&gt;
&lt;li&gt;Some(T) 表示为指针值。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;因此，&lt;code&gt;Option&amp;lt;&amp;amp;T&amp;gt;&lt;/code&gt; 与 &lt;code&gt;&amp;amp;T&lt;/code&gt; 的大小是相同的，不需要额外的内存。&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;mem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;some_value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;i32&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;none_value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;i32&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Size of Option&amp;lt;&amp;amp;i32&amp;gt;: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;mem&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;size_of&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;i32&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt; &lt;span class="c1"&gt;// 输出 8，即一个指针（地址）的长度&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;some_value&lt;/code&gt; 与 &lt;code&gt;none_value&lt;/code&gt; 所对应的内存布局图：
&lt;img src="/uploads/photo/james/39fcf109-f2c7-4134-9cba-815e0392363d.jpg!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;可见，对于 &lt;code&gt;Option&amp;lt;&amp;amp;T&amp;gt;&lt;/code&gt; 类型，即使包含两种状态，其大小与普通指针一致。&lt;/p&gt;

&lt;p&gt;对于枚举的布局问题，我们简单总结如下：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;枚举的总大小是 &lt;strong&gt;最大变体的大小&lt;/strong&gt; 加上 &lt;strong&gt;对齐后标签的大小&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;Rust 不会强行将标签扩展到与对齐值一致，只要对齐要求满足即可。&lt;/li&gt;
&lt;li&gt;这种优化节省了内存，同时仍然保持了内存对齐的正确性。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="总结"&gt;总结&lt;/h2&gt;
&lt;p&gt;本文系统地讨论了枚举相关的话题，主要包括枚举的意义、枚举的定义与实例化、&lt;code&gt;Option&lt;/code&gt;和&lt;code&gt;Result&lt;/code&gt;等内容。在文章后部分，还就如何使用&lt;code&gt;match&lt;/code&gt;、&lt;code&gt;if let&lt;/code&gt;控制流运算符操作枚举、枚举的内存布局等内容，进行了简要地介绍。枚举与结构体一样，都是构建 Rust 程序的重要构件，希望本文能对正在学习 Rust 的您有好所帮助。&lt;/p&gt;</description>
      <author>james</author>
      <pubDate>Sat, 30 Nov 2024 09:48:40 +0800</pubDate>
      <link>https://soldev.cn/topics/108</link>
      <guid>https://soldev.cn/topics/108</guid>
    </item>
    <item>
      <title>如筑如堵，如埤如坻 —— Rust 中的结构体</title>
      <description>&lt;p&gt;hi，好久不见！有近半年未更新了。我先自罚三杯吧。&lt;/p&gt;

&lt;p&gt;一杯是为了农田里犁头与泥土的约定，一杯是为了地铁里被挤压的青春。最后一杯，为了莫名者散落于代码间的悲喜。&lt;/p&gt;

&lt;p&gt;今天要讨论的话题是，结构体 (Struct)。摸着良心讲，结构体真的很简单！但作为一个旨在入门的系列教程，没有理由把它绕过去。个人觉得，越是简单的东西，越是不好写。在写这篇文章之前，我几经徘徊，无从下手。到目前为止，此文可能是最差的那一篇吧。发布出来，权当抛砖引玉吧！&lt;/p&gt;

&lt;p&gt;我们知道，内存里存放的是无差别的 0 与 1 的序列。“天地不仁，以万物为刍狗”。这是一片混沌的世界，在内存的眼里，众生平等，无界无相无色，无贵贱亦无高下。为了给这个混沌的世界带来光，Rust 提供了基本数据类型（比如整型、字符等），这样就可以对 0 与 1 的序列进行有意义的解读。至此，混沌的世界有了生气。但仅仅依靠这些基本数据类型，不足以 (或者说不方便) 构建绚丽多彩的世界。于是，Rust 又提供了另一个神器，让我们能够自定义各种数据类型以建模业务世界。这个神器，就是自定义类型，在 Rust 中包括&lt;code&gt;结构体(Struct)、枚举(Enum)、联合体(Union)&lt;/code&gt;。当然，本文只说结构体，嗯。&lt;/p&gt;

&lt;p&gt;可以通过结构体来自定义要想的数据类型，它刻画了这个类型应当由哪些部分组成、各个部分对应的名字与类型以及作为结构体整体上所应支持的行为，然后，它将作为一个整体被 CPU 所理解与使用。如果把基本数据类型比作是建构房屋的砂石、水泥、钢筋、砖块等单元材料，那么结构体就是由这些单元定制的各种预制构件。&lt;/p&gt;

&lt;p&gt;好了，故事讲得差不多了。现在让我们来看看在 Rust 中结构体 (Struct) 的具体情况吧。&lt;/p&gt;
&lt;h2 id="结构体的定义"&gt;结构体的定义&lt;/h2&gt;
&lt;p&gt;在 Rust 中结构体的定义语法如下：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;StructName&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;//名称以大小字母开头，采取驼峰式拼写&lt;/span&gt;
    &lt;span class="n"&gt;field1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;data_type1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;field2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;data_type2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;field3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;data_type3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;结构体定义以&lt;code&gt;struct&lt;/code&gt;关键字开头，后面紧跟着结构体的名字（名字的首字母通常大写），然后在名字后跟上一对花括号（&lt;code&gt;{&lt;/code&gt;通常不换行）。在花括号内，列出你希望结构体所包含的内容，这些内容是由一条条称为字段的东西组成的。比如 &lt;code&gt;field1: data_type1,&lt;/code&gt;，这就是一条字段，它定义了一个叫做&lt;code&gt;field1&lt;/code&gt;字段，它所对应的类型是&lt;code&gt;data_type1&lt;/code&gt;。各个字段之间用&lt;code&gt;,&lt;/code&gt;分隔，作为一种习惯，即使是最后一个字段的后面也会默认带上&lt;code&gt;,&lt;/code&gt;，这么做的好处也是显然的，比如要在一个已有结构体中插入或者新增一个字段时至少可以少按一次键盘😁。一个字段的类型可以是基本类型，也可以其他已经定义好的结构体类型，还可以是指向正在定义的本结构体的指针类型。&lt;/p&gt;

&lt;p&gt;嗯，这就是结构体定义的全部内容了，是不是很简单呢？现在来看一个具体的例子吧。&lt;/p&gt;

&lt;p&gt;在具体写代码之前，我想交代一下背景，这可能会贯穿到我们后续的一系列文章中。我们正在用 Rust 写一部中国农耕文化史，记录我们记忆深处的故事。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;话说大明洪武年间，在湖广省辰州府有一座圣人山，顶上有一冬瓜岭，这里居住一个叫刘一明的人，时年三十又三。说起刘一明，有一段来历。一明本系江西吉安府人，两年前迁于此。公子聪慧过人，自幼读圣贤，十六即中秀才。然因时运不济，屡试不第，至而立仍为布衣，且孑然一身。某夜梦一赤眉仙人，身披丹甲，脚踏祥云，头生两角如钳，不知何方神圣。仙人劝戒一明远离功名，并赐诗一首与他，诗云：金陵三月好风光，秦淮烟柳转眼空。焚书西行遇圣人，枕瓜而眠粮万顷。梦醒，一明顿悟，其心犹云破月来。遂焚书十担余，誓忘功名。是年洪武廿‌六年，岁次癸酉，适逢“洪武移民”，一明随众一路西行。逾月余，至一千又二百余里处，忽遇一山。巍巍然齐天，白云束腰，望之或有仙气。问之何山？答曰：圣人山。又指白云生处，复问之何地？答曰：冬瓜岭也。一明甚喜，岂非“枕瓜而眠”乎？遂伐木结庐、累石起灶定居于岭上，拓荒开路、播谷栽菽，已二载余也。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;这是一段从书本到农田的弃文从农之路。辛酸无奈，但又洒脱从容，还有希望与欢愉。是的，农田一直是勇者乐园，乡村始终是本真的起点。我们的文化，就在那泥土的一翻一覆间；我们的情怀，就在那锄犁的声声碰撞中。寺庙与朝堂，只是懦夫、窃盗者与流氓们的临时庇护所。&lt;/p&gt;

&lt;p&gt;对不起，我知道已经跑题了，让我们立马回归正题。刘先生为方便出行与驮运农货，花了 12 两白银，托人从二百里余外的宝庆府马贩张老六处买了一匹白马。现在让我们为其设计一个数据类型。稍加思考，其代码可能如下：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Horse&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;//给这个新的类型取了一个名字:Horse&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;//马的名字，假设马的数量不会太多，主人用不同名字就可以区别&lt;/span&gt;
    &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;//马的年龄，以月计算&lt;/span&gt;
    &lt;span class="n"&gt;gender&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// 马的性别，规定1是公马、0是母马&lt;/span&gt;
    &lt;span class="n"&gt;skin_color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;//马的肤色，比如白马、黑马、枣红色、或者是杂色等&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;很简单，是吧。现在，一个全新的数据类型 Horse 就定义好了。&lt;/p&gt;
&lt;h2 id="结构体的实例化"&gt;结构体的实例化&lt;/h2&gt;
&lt;p&gt;那应该怎么去使用这个类型呢？如何用马这个类型，去创建或者说记录一个实实在在的马呢？这就是结构体实例化问题。&lt;/p&gt;

&lt;p&gt;在 Rust 中实例化结构体的完整语法如下：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="n"&gt;SomeStructName&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;field1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;field1_value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;field2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;field2_value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;field3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;field3_value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们要实例化（记录）老刘买的小白马，其代码如下：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;white_horse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Horse&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"丰行"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="c1"&gt;//"丰行"，寓意着五谷丰登&lt;/span&gt;
        &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;26&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;gender&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// 1 表示是公马&lt;/span&gt;
        &lt;span class="n"&gt;skin_color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"白色"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以通过&lt;code&gt;.&lt;/code&gt;运算符，从结构体实例中获取某个字段的信息。比如想知道上面这匹马的年纪：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;white_horse&lt;/span&gt;&lt;span class="py"&gt;.age&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果要修改结构体实例中的某个字段信息，可以直接对字段进行赋值。比如要更改马的年纪：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="n"&gt;white_horse&lt;/span&gt;&lt;span class="py"&gt;.age&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;27&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;由于结构体中各个字段是以结构体作为一个整体被使用的，修改某个字段信息，等同于修改了这个结构体实例。所以要使一个结构体实例能被修改，必须要符合我们之前所说的有关变量可变性的规定，也就是说，在定义&lt;code&gt;white_horse&lt;/code&gt;变量时必须加上&lt;code&gt;mut&lt;/code&gt;关键字，即&lt;code&gt;let mut white_horse = ...&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;在一个函数或是方法中实例化一个结构体，如果字段的名称与其对应的欲赋值参数或者上下文中的变量名称相同，则可以省略字段名，直接写上参数或变量即可。例如：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 参数与字段同名&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;build_horse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Horse&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Horse&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="c1"&gt;//欲赋值变量与字段同名，都是name，省略字段名&lt;/span&gt;
        &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="c1"&gt;//欲赋值变量与字段同名，都是age，省略字段名&lt;/span&gt;
        &lt;span class="n"&gt;gender&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;skin_color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"白色"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="c1"&gt;// 上下文中的变量与字段同名&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;28_u8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"丰行"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;gender&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1_u8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;horse2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Horse&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;gender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;skin_color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"白色"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;当然，也可以通过一个已有的实例，来实例化一个新的结构体实例。刘一明同志，闻鸡而出、披星而归，加上精打细算，两年后手上又有了一大笔积蓄。为扩大生产，又找张老六购得一匹新马。此马与之前的白马同年同月同日生，也是公马。因其浑身枣红，老刘给它取名“赤瑶”，寓意红红火火、事业爱情双丰收。&lt;/p&gt;

&lt;p&gt;现在，请我们为老刘记录一下这匹新买的马。代码可能如下：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;red_horse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Horse&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"赤瑶"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;skin_color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"枣红"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="n"&gt;white_horse&lt;/span&gt; &lt;span class="c1"&gt;//注意：后面不需要加`,`&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;以上的代码创建了一个新马 &lt;code&gt;red_horse&lt;/code&gt;，该实例只是名字与颜色不同于白马，其他的信息是一样的（&lt;code&gt;age、 gender&lt;/code&gt; 字段的值与 &lt;code&gt;white_horse&lt;/code&gt; 相同）。 &lt;code&gt;..white_horse&lt;/code&gt; 必须放在最后，以指定其余的字段应从 &lt;code&gt;white_horse&lt;/code&gt; 的相应字段中获取其值，但我们可以选择以任何顺序为任意字段指定值，而不用考虑结构体定义中字段的顺序。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;...white_horse&lt;/code&gt;，事实是只是一个语法糖。它表示对于欲实例化实例中未显式列出的字段，其值将用&lt;code&gt;white_horse&lt;/code&gt;中对应的字段进行赋值。上面代码在背后的所对应的实际代码大概如下：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;red_horse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Horse&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"赤瑶"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;skin_color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"枣红"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="c1"&gt;//..white_horse 等价下面的代码&lt;/span&gt;
        &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;white_horse&lt;/span&gt;&lt;span class="py"&gt;.age&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="n"&gt;gender&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;white_horse&lt;/span&gt;&lt;span class="py"&gt;.gender&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里有一个问题需要注意，由于&lt;code&gt;...white_horse&lt;/code&gt;是把一个实例中的字段值赋值另一个实例的字段，本质上是赋值行为，那么当然要遵循 Rust 中有关所有权的规定。换句话说，因字段类型的性质不同，当赋值时，要么发生复制行为，要么发生移动行为。如果是移动，意味着原变量将不可用，因为其相应字段信息被移走了（相当于这个结构体实例的这人字段，被人挖空了😜）。当然，我们上面的例子不会有问题，因为&lt;code&gt;age&lt;/code&gt;与&lt;code&gt;gender&lt;/code&gt;的类型为基本数据类型（均实现了&lt;code&gt;Copy&lt;/code&gt;特征，关于特征后续会开专文讨论），在赋值时是复制行为，所以使用&lt;code&gt;white_horse&lt;/code&gt;协助实例化&lt;code&gt;red_horse&lt;/code&gt;后，&lt;code&gt;white_horse&lt;/code&gt;仍然可用。&lt;/p&gt;

&lt;p&gt;如果我们把代码修改一下，情况就不同了。比如下面的代码示例：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt;  &lt;span class="n"&gt;white_horse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Horse&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"丰行"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;26&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;gender&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;skin_color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"白色"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;


    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;red_horse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Horse&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"赤瑶"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="c1"&gt;// 注释下面这一行，表示red_horse的颜色与white_horse 一样，赋值时发生移动行为&lt;/span&gt;
        &lt;span class="c1"&gt;//skin_color: "枣红".to_string(),&lt;/span&gt;
        &lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="n"&gt;white_horse&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;


    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;white_horse&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上述代码，将无法通过编译，会提示类似&lt;code&gt;value borrowed here after partial move&lt;/code&gt;的错误。白马的毛色信息赋值给红马的时候，其效果好比是把白马的毛全部拔光了，😂哈哈！当然，这不是我们今天的主题，只是在这里顺便复习一下之前的内容。&lt;/p&gt;
&lt;h2 id="关联函数与关联方法"&gt;关联函数与关联方法&lt;/h2&gt;
&lt;p&gt;结构体，不光可以包含字段，还可以与函数相关联。结构体实例化后，在内存中表现为一连串的二进制序列。函数，通常被加载到内存中一个称为&lt;code&gt;代码段&lt;/code&gt;地方，函数名称作为函数调度的单位，其事实上只是一个指针类型的实例，代表了对应函数代码序列的起始处地址。可见，在内存的角度看，两者是没有任何关系的。一个，面朝大海；一个，头顶青山。但为了表达一个结构体具备某种行为能力，即该类型或该类型的实例可以调用哪些函数，需要将两者关联起来。这样，这些函数在逻辑上就隶属于某个特定的结构体类型。这就是所谓的关联的实质，关联函数与关联方法在技术实现层面是一样的，只有概念与目的上的区别。&lt;/p&gt;
&lt;h3 id="关联函数"&gt;关联函数&lt;/h3&gt;
&lt;p&gt;关联函数的语法如下：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;SomeStructName&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;func1_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Arg1Type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;arg2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Arg2Type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ReturnType&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;func2_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Arg1Type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;arg2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Arg2Type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ReturnType&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;func3_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Arg1Type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;arg2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Arg2Type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ReturnType&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;要为一个结构体实现关联函数，使用&lt;code&gt;impl&lt;/code&gt;关键字，在后面紧跟结构体名，然后用一对花括号开启一个代码区。在这对花括号中定义的函数，就是该类型的关系函数。函数定义的规则，与常规的函数定义没有什么区别。&lt;/p&gt;

&lt;p&gt;现在，我们为马这个结构体来定义一个关联方法&lt;code&gt;new&lt;/code&gt;：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;Horse&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gender&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;skin_color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Horse&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;gender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;skin_color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;以上的代码中的&lt;code&gt;new&lt;/code&gt;函数不是一个普通的函数，而是&lt;code&gt;Horse&lt;/code&gt;的关联函数。该函数的功能是根据传入的参数，实例化一个 Horse 实例。这个函数有两点需要稍微解释一下，一是返回类型：&lt;code&gt;Self&lt;/code&gt;，它是一种语法糖，等同于所关联的结构体的类型，在上例中就是&lt;code&gt;Horse&lt;/code&gt;。另一个，就是函数内创建的实例的代码&lt;code&gt;Horse{...}&lt;/code&gt;后面没有跟上&lt;code&gt;;&lt;/code&gt;，它表示创建并返回，相当于 &lt;code&gt;return Horse{...};&lt;/code&gt; 。&lt;/p&gt;

&lt;p&gt;使用关联函数的例子，比如下面代码，我们使用上面的定义的&lt;code&gt;new&lt;/code&gt;函数。&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;horse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Horse&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;"丰行"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="mi"&gt;28_u8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"白色"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在以上代码中，我们在调用&lt;code&gt;new&lt;/code&gt;函数的前面加上了前缀&lt;code&gt;Horse::&lt;/code&gt;，它表明&lt;code&gt;new&lt;/code&gt;不是一个普通的函数，而是一个关联函数，其在逻辑上隶属于&lt;code&gt;Horse&lt;/code&gt;类。所以，我们调用关联函数的时候，既要指明它所隶属的类型信息，还要指明函数本身的函数名信息。其中的&lt;code&gt;::&lt;/code&gt;符号表示后者在前者的范围内。但在编译后的可执行文件中所包括的符号信息，可能会是类似：&lt;code&gt;_some_prefix_horse_new_(...)&lt;/code&gt;形式的名称，可见关联函数本质还是一种普通函数，只是为表达既定的语义，编译作了额外的支持。&lt;/p&gt;
&lt;h3 id="关联方法"&gt;关联方法&lt;/h3&gt;
&lt;p&gt;关联方法跟关联函数基本上是一样的，它们之间的区别仅仅在函数的参数上。这个区别，是由关联方法与关联函数在概念上的区别所导致的。关联函数，强调的是一个函数隶属于一个类型，相当于其他语言中的静态方法。而关联方法，则强调的是一个函数隶属于一个类型的实例，这一点与其他大部分的面向对象语言中的方法是一致的。&lt;/p&gt;

&lt;p&gt;关联方法要求所定义的函数的参数中，有一个参数必须是&lt;code&gt;self/&amp;amp;self/&amp;amp;mut self&lt;/code&gt;中的一种，这样，该方法就与实例之间存在一种关联了。其中&lt;code&gt;self&lt;/code&gt;是&lt;code&gt;self: Self&lt;/code&gt;的语法糖，表示调用方法的实例，其他两者分别表示调用方法的实例的不可变与可变引用。在&lt;code&gt;impl SomeStruct&lt;/code&gt;代码区内定义的函数，如果满足上面对于参数的要求，那么它就是这个类型的关系方法，否则就是关联函数。&lt;/p&gt;

&lt;p&gt;下面，举几个关联方法的例子吧。&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;Horse&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="c1"&gt;// 获取马儿的名字&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;get_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.name&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;name&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// 设置马儿的名字&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;set_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// 马儿在奔跑...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;carry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;goods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u16&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// 马儿驮运货物&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;下面是具体使用这些关联方法的示例。&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt;  &lt;span class="n"&gt;horse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Horse&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;"丰行"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="mi"&gt;28_u8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"白色"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// 获取马儿的姓名&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;horse&lt;/span&gt;&lt;span class="nf"&gt;.get_name&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="c1"&gt;//重新设置马儿的年龄&lt;/span&gt;
    &lt;span class="n"&gt;horse&lt;/span&gt;&lt;span class="nf"&gt;.set_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"赤瑶"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

    &lt;span class="c1"&gt;// 让马儿驮运 50kg 的货物&lt;/span&gt;
    &lt;span class="n"&gt;horse&lt;/span&gt;&lt;span class="nf"&gt;.carry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// 让马儿奔跑起来，由于参数是self,运行该方法后，horse实例的所有权被移走&lt;/span&gt;
    &lt;span class="n"&gt;horse&lt;/span&gt;&lt;span class="nf"&gt;.run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// 下面代码将无法通过编译，horse因调用run()而失效。&lt;/span&gt;
    &lt;span class="c1"&gt;//_ = horse.get_name();&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上面代码展示了如何用&lt;code&gt;instance.method()&lt;/code&gt;方式调用一个关联方法，这种调用方式很符合人类的思维方式，感觉&lt;code&gt;instance&lt;/code&gt;拥有了&lt;code&gt;method&lt;/code&gt;这种行为，调用起来甚是自然。
事实上，&lt;code&gt;instance.method()&lt;/code&gt;只是一种语法糖，编译器在背后会把它转换成：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nn"&gt;SomeType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="o"&gt;/&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="o"&gt;/&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;other_rest_args&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;比如，对于下面的代码：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="n"&gt;horse&lt;/span&gt;&lt;span class="nf"&gt;.carry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;编译器会把它转换成：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nn"&gt;Horse&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;carry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;horse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;编译器的这种转换，为程序员提供极大的便利，它会自动为&lt;code&gt;instance&lt;/code&gt; 添加 &lt;code&gt;&amp;amp;、&amp;amp;mut&lt;/code&gt; 或 &lt;code&gt;*&lt;/code&gt; 以便使 &lt;code&gt;instance&lt;/code&gt; 与方法签名匹配。比如，上面的代码中，&lt;code&gt;horse&lt;/code&gt;是&lt;code&gt;self&lt;/code&gt;类型，而&lt;code&gt;carry&lt;/code&gt;要求的参数是&lt;code&gt;&amp;amp;self&lt;/code&gt;，当直接用&lt;code&gt;self&lt;/code&gt;类型的实例调用方法时，编译器自动用实例的引用（&lt;code&gt;&amp;amp;self&lt;/code&gt;）传递到方法中。同理，如果参数要求是&lt;code&gt;self&lt;/code&gt;，而调用者是&lt;code&gt;&amp;amp;self&lt;/code&gt;，编译器会先把引用先解引用后&lt;code&gt;*self&lt;/code&gt;），再把得到的本体传递到方法中。&lt;/p&gt;

&lt;p&gt;对于一个结构体，可以使用&lt;code&gt;impl&lt;/code&gt;为其开辟多个代码区，用于划分不同的的业务逻辑。在代码区内，既可以写关联函数，也可以写关联方法。&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;Horse&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="c1"&gt;// 基本属性相关的代码区&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;Horse&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="c1"&gt;// 出行相关的代码区&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;Horse&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="c1"&gt;// 驮运农货相关的代码区&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;对于关联方法，最后一个需要提及的问题是，要注意在调用方法时的所有权转移问题。就是，如果一个类型没有实现&lt;code&gt;Copy&lt;/code&gt;特征，当调用一个参数要求是&lt;code&gt;self&lt;/code&gt; 的方法后，实例的所有权将发生转移。也这是，上面示例代码中，当调用&lt;code&gt;horse.run()&lt;/code&gt;后，&lt;code&gt;horse&lt;/code&gt;实例不可再用的原因。可见，所有权原则在 Rust 中无处不在！不熟悉的朋友，敬请查看我之前的相关文章。&lt;/p&gt;
&lt;h2 id="几个特殊的结构体"&gt;几个特殊的结构体&lt;/h2&gt;&lt;h3 id="元组结构体"&gt;元组结构体&lt;/h3&gt;
&lt;p&gt;元组结构体也是一种结构体，同样有自己的名字，只是没有具体的字段名，只有字段的类型。当你想给整个元组取一个名字，并使元组成为与其他元组不同的类型时，元组结构体是很有用的。&lt;/p&gt;

&lt;p&gt;要定义元组结构体，以&lt;code&gt;struct&lt;/code&gt;关键字和&lt;code&gt;结构体名&lt;/code&gt;开头，后面紧跟着一个元组类型。下面是两个分别叫做 &lt;code&gt;Color&lt;/code&gt; 和 &lt;code&gt;Point&lt;/code&gt; 元组结构体的定义和用法：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nf"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nf"&gt;Point&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;black&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;origin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Point&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;注意 &lt;code&gt;black&lt;/code&gt; 和 &lt;code&gt;origin&lt;/code&gt;值的类型不同，因为它们是不同的元组结构体的实例。你定义的每一个结构体有其自己的类型，即使结构体中的字段有着相同的类型。例如，一个获取 &lt;code&gt;Color&lt;/code&gt; 类型参数的函数不能接受 &lt;code&gt;Point&lt;/code&gt; 作为参数，即便这两个类型都由三个 &lt;code&gt;i32&lt;/code&gt; 值组成。在其他方面，元组结构体实例类似于元组：可以将其解构为单独的部分，也可以使用 &lt;code&gt;.&lt;/code&gt; 后跟索引来访问单独的值。&lt;/p&gt;
&lt;h3 id="单元结构体"&gt;单元结构体&lt;/h3&gt;
&lt;p&gt;单元结构体，没有包含有任何字段，仅仅只提供一个名字，它类似于空元组&lt;code&gt;()&lt;/code&gt;类型。既然单元结构体不包含字段，那么设计这种类型的用意思是什么呢？如果你看了前面的内容，你应该知道结构体除了字段信息外，还有一个重要的信息就是关系函数或方法。对，设计这种类型之目的，就是为了给它增加关联函数或关联方法。当然也可以为它实现特征（Trait），我们后面会有专文讨论特征的话题。现在你只需要知道，所谓的特征在本质上也算是一种关联方法，只是在关联方法的基础上增加了一些限制罢了。&lt;/p&gt;

&lt;p&gt;下在是一个单元结构体的例子。&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;UnitStruct&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;UnitStruct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;do_something&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;unit_object&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;UnitStruct&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;unit_object&lt;/span&gt;&lt;span class="nf"&gt;.do_something&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可见，单元结构体除了没有字段外，与普通的结构体没有什么区别。&lt;/p&gt;
&lt;h2 id="结体体的可见性"&gt;结体体的可见性&lt;/h2&gt;
&lt;p&gt;可见性是 Rust 中的重要内容，后续会开专文讨论。在这里，仅仅简单地说一下与结构体相关的内容。在讨论这个话题之前，我先声明一下：接下来的言辞，可能不精准，但不会影响对后续相关内容的理解。&lt;/p&gt;

&lt;p&gt;所谓的可见性，是指一个类型能被访问的范围。对于结构体来说，主要有两层含义。一是，该结构体类型作为一个整体能否被外界访问；二是，如果该结构体可以被访问，那么哪些字段可以被外界访问？哪些字段不能被访问？&lt;/p&gt;

&lt;p&gt;总体的情况是，结构体及其字段、关系函数和方法的可见性，大致可分为两个级别。一个是&lt;code&gt;公共的（用pub关键字标）&lt;/code&gt;，另一个是&lt;code&gt;私有（默认）&lt;/code&gt;的。对于公开级别的，如果定义该结构体所处的模块能被使用者所在模块可见，那么对应被标记的结构体或字段也能被使用者访问，否则不可访问。对于私有级别，仅能在定义该结构体的当前模块内被访问。&lt;/p&gt;

&lt;p&gt;先看一例子吧。&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// main.rs&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Horse&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;gender&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;skin_color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;Horse&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gender&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;skin_color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Horse&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;gender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;skin_color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;carry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;goods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u16&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;



&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;horse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Horse&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"丰行"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="mi"&gt;28&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"白色"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;horse&lt;/span&gt;&lt;span class="py"&gt;.name&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;horse&lt;/span&gt;&lt;span class="nf"&gt;.carry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;以上代码可以正常通过编译，在&lt;code&gt;main&lt;/code&gt;函数中可以访问&lt;code&gt;Horse&lt;/code&gt;类型及其关联函数与方法。这是因为，&lt;code&gt;mian&lt;/code&gt;函数与结构体的定义在同一个&lt;code&gt;main.rs&lt;/code&gt;文件中，虽然结构体以及内部的字段和关联函数与方法都是私有的（没有加上&lt;code&gt;pub&lt;/code&gt;关键字）。&lt;/p&gt;

&lt;p&gt;再举一个例子。&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="cd"&gt;/// horse.rs&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Horse&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;gender&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;skin_color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;Horse&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gender&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;skin_color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Horse&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;gender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;skin_color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;carry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;goods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u16&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="cd"&gt;/// main.rs&lt;/span&gt;
&lt;span class="k"&gt;mod&lt;/span&gt; &lt;span class="n"&gt;horse&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;horse&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 先忽略此行&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;horse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Horse&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"丰行"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="mi"&gt;28&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"白色"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;horse&lt;/span&gt;&lt;span class="py"&gt;.name&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;horse&lt;/span&gt;&lt;span class="nf"&gt;.carry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这一次，我们把定义马的逻辑抽到了&lt;code&gt;horse.rs&lt;/code&gt;中，并通过&lt;code&gt;mod horse&lt;/code&gt;导入至&lt;code&gt;main&lt;/code&gt;中作用域中。编译上面的代码，会出现类似：&lt;code&gt;failed to resolve: use of undeclared type Horse&lt;/code&gt;的错误信息，表明不能识别&lt;code&gt;Horse&lt;/code&gt;类型，也就是说&lt;code&gt;Horse&lt;/code&gt;类型不能被外界访问。因为，我们的&lt;code&gt;Horse&lt;/code&gt;默认的可见性是私有的，其仅能在&lt;code&gt;hosre&lt;/code&gt;模块内被访问。要想让上面的&lt;code&gt;main&lt;/code&gt;函数通过编译，必须修改代码。修改后可可编译的代码如下：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="cd"&gt;/// horse.rs&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Horse&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;//加上pub，以便外界可访问&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;//加上pub，以便外界可访问&lt;/span&gt;
    &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;gender&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;skin_color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;Horse&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gender&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;skin_color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Horse&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;gender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;skin_color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="c1"&gt;//加上pub，以便外界可访问&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;carry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;goods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u16&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可见，想对外公开，只需在前面加上&lt;code&gt;pub&lt;/code&gt; 关键字。好了，这就是有关结构体中可见性的情况了。&lt;/p&gt;

&lt;p&gt;让我们来作个简单的总结：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;结构体，默认情况是私有的，仅在定义它的模块内可见，若要公开需加上&lt;code&gt;pub&lt;/code&gt;关键字。如果结构体本身是私有的，其字段与关联方法必然也是不可访问的。&lt;/li&gt;
&lt;li&gt;结构体的各个字段，有自己的可见性，默认情况是私有的，若要公开需加上&lt;code&gt;pub&lt;/code&gt;关键字。&lt;/li&gt;
&lt;li&gt;结构体的关联函数与方法，有自己的可见性，默认情况是私有的，若要公开需加上&lt;code&gt;pub&lt;/code&gt;关键字。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="结构体的内存布局"&gt;结构体的内存布局&lt;/h2&gt;
&lt;p&gt;所谓的内存布局，是指结构实例中的字段在内存中是如何被排列的，以及该实例最终所占用的内存空间尺寸。这里涉及到一个重要的话题，就是内存对齐问题。至于什么是内存对齐，以及为什么需要内存对齐，我不想做过多的讨论，要彻底地讲清楚可能需要开一篇专文。在这里打一个不是很恰当也不准确的比方，但可以方便我们理解。把数据比做是物品，把内存比做是一个连续的场地，场地上画了一条线性排列的格子，每一个格子都标有序号用以表示它的位置，物品整齐地摆放在场地的格子里。把 CPU 比做人，人与这些物品打交道时，人需要走到这些格子上去查看或者拿取物品。假设人走路时每走一步可以跨越 N 个格子，那么如果一个数据从其开始到结束点的长度，处在两个步伐的交叉处，就很不方便，自然会影响工作效率。最理想的情况是，这些数据尽量都放在步行的起始处（或步长内）。&lt;/p&gt;
&lt;h3 id="常规内存布局"&gt;常规内存布局&lt;/h3&gt;
&lt;p&gt;Rust 内存布局问题的关键，是内存对齐。内存对齐问题，分&lt;code&gt;基本数据类型&lt;/code&gt;与&lt;code&gt;自定义类型&lt;/code&gt;两种情况，现在我们分别来讨论。&lt;/p&gt;
&lt;h4 id="基本类型的对齐"&gt;基本类型的对齐&lt;/h4&gt;
&lt;p&gt;比如 &lt;code&gt;u32&lt;/code&gt; 需要在内存中以 4 字节对齐，&lt;code&gt;u8&lt;/code&gt; 类型则需要 1 字节对齐。通常一个占用 &lt;code&gt;N&lt;/code&gt; 个字节的类型，它的实例就是按 &lt;code&gt;N&lt;/code&gt; 个字节对齐。按 &lt;code&gt;N&lt;/code&gt; 个字节对齐的意思是，存放它的地址必须是对齐值&lt;code&gt;N&lt;/code&gt;的整数倍，当 &lt;code&gt;N&lt;/code&gt; 为 1 时，表示可以存储在任意位置。&lt;/p&gt;
&lt;h4 id="结构体的对齐"&gt;结构体的对齐&lt;/h4&gt;
&lt;p&gt;结构体作为一个独立的数据类型，它的对齐值取决于其所包含的字段类型。会以所包含字段中类型长度最大的值，作为整个结构体的对齐值。然后再按照这个对齐值，对字段进行排列组合，再进行填充（padding)。结构体最后所占用的内存长度，等于各字段原本的数据长度，再加上，所有字段的 padding 长度的总和。&lt;/p&gt;

&lt;p&gt;看一个实际的例子：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[derive(Debug)]&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Example&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// 1 字节&lt;/span&gt;
    &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// 4 字节&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// 2 字节&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Example&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Example size: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;mem&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;size_of&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Example&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt; &lt;span class="c1"&gt;// 输出大小&lt;/span&gt;
    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Align of struct: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;mem&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;align_of&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Example&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt; &lt;span class="c1"&gt;// 输出对齐值&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;运行上面程序，会输出：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Example size: 8
Align of struct: 4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;表明结构体&lt;code&gt;Example&lt;/code&gt;实例在内存占用 8 个字节，整个结构体的对齐值为 4 个字节。实例&lt;code&gt;ex&lt;/code&gt;在内存中的具体布局如下图所示：
&lt;img src="/uploads/photo/james/50b70d18-041c-4ed5-8cab-881dcc79c2d6.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;如果我们把&lt;code&gt;b:u32&lt;/code&gt; 改为&lt;code&gt;b: u64&lt;/code&gt;，程序会输出：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Example size: 16
Align of struct: 8
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;此时，内存布局如下：
&lt;img src="/uploads/photo/james/1a9104da-c17c-4197-b475-b4e73cf4112f.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;通过这个例子，我们可以得到正常情况下，Rust 中结构体实例在内存布局的规律：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;编译器根据节约内存的原则，结合对齐值，会对字段的顺序进行重新排列（不需要人为排序）。&lt;/li&gt;
&lt;li&gt;结构体的对齐值，为所包含字段中的最大的那一个（多个字段长度的最小公倍数，通常为 2 的 N 次幂）。&lt;/li&gt;
&lt;li&gt;&lt;p&gt;如果一个结构体的对齐值是 &lt;code&gt;N&lt;/code&gt; ，那么结构体的总长度必须是 &lt;code&gt;N&lt;/code&gt; 的整数倍。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;为了匹配对齐值，编译器会根据情况把多个字段合并在一起，并插入&lt;code&gt;0&lt;/code&gt;作为 padding。如果是最后一个字段，不会插入填充值，比如上图中的字段&lt;code&gt;a:1&lt;/code&gt;，其后面的&lt;code&gt;b04ae7b78f&lt;/code&gt; 是内存中原本的内容。这是可以理解的，因为当访问&lt;code&gt;ex.a&lt;/code&gt;的时候，已经找它的起始地址，再选择一个字节的长度（&lt;code&gt;u8&lt;/code&gt;的宽度）即可，后面的信息自然可以忽略掉。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="几个特殊的内存布局"&gt;几个特殊的内存布局&lt;/h3&gt;
&lt;p&gt;Rust 为我们提供另外几种内存布局模式，用于某些特定的场景。&lt;/p&gt;
&lt;h4 id="#[repr(align(N))]"&gt;&lt;code&gt;#[repr(align(N))]&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;#[repr(align(N))]&lt;/code&gt; 允许我们显式地为结构体或类型指定一个对齐值，这对于优化内存布局或者确保特定的对齐需求非常有用。该属性将确保该类型的对齐值为&lt;code&gt;N&lt;/code&gt;个字节。&lt;/p&gt;

&lt;p&gt;假设我们想要确保结构体按照 8 字节对齐：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[repr(align(&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="nd"&gt;))]&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;AlignedStruct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Size of AlignedStruct: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;mem&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;size_of&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;AlignedStruct&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Align of AlignedStruct: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;mem&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;align_of&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;AlignedStruct&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;输出：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Size of AlignedStruct: 8
Align of AlignedStruct: 8
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;该类型实例的内存布局如下图：
&lt;img src="/uploads/photo/james/6710d09a-8045-4dc2-b0a9-8a54169af71c.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h4 id="#[repr(C)]"&gt;&lt;code&gt;#[repr(C)]&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;#[repr(C)]&lt;/code&gt;可以确保 Rust 结构体的内存布局与 C 语言的结构体布局一致。这对于与 C 语言进行 FFI（外部函数接口）时非常重要。C 语言的结构体通常要求严格的内存布局，因此 Rust 必须遵循与 C 相同的规则。&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt; &lt;span class="nd"&gt;#[repr(C)]&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Example&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;FFI 是一个很重要的内容，现在你只需要知道是用来和 C 交互的就行了。C 中结构体布局问题请查阅其相关的内容。&lt;/p&gt;

&lt;p&gt;#### &lt;code&gt;#[repr(transparent)]&lt;/code&gt;
 &lt;code&gt;#[repr(transparent)]&lt;/code&gt;是 Rust 中用于新类型模式的一种标记，它用于指示一个结构体或枚举具有透明的内存布局，也就是说，结构体的内存布局与其唯一字段的内存布局完全相同。该标记常用于封装底层类型时，避免对齐和大小的改变，从而保持与底层类型相同的内存布局。 &lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt; &lt;span class="nd"&gt;#[repr(transparent)]&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nf"&gt;MyU32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;u32&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Size of MyU32: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;mem&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;size_of&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MyU32&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Align of MyU32: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;mem&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;align_of&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MyU32&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;输出：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; Size of MyU32: 4
Align of MyU32: 4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这会使得结构体 &lt;code&gt;MyU32&lt;/code&gt; 的内存布局与其唯一字段 &lt;code&gt;u32&lt;/code&gt; 完全相同，且不会引入任何额外的填充字节或对齐要求的改变。这样做的好处是，便于与 C 语言等其他语言进行互操作。如果我们希望在 Rust 中封装一个 C 语言中的类型，并确保其内存布局与 C 类型兼容，&lt;code&gt;#[repr(transparent)]&lt;/code&gt; 是一个理想的选择。&lt;/p&gt;

&lt;p&gt;#### &lt;code&gt;#[repr(packed)]&lt;/code&gt;
 &lt;code&gt;#[repr(packed)]&lt;/code&gt; 属性可以让 Rust 编译器取消结构体中的填充字节，使得结构体紧凑地排列。&lt;strong&gt;这种方式可能会导致字段不符合其自然对齐要求，因此在访问这些字段时需要特别小心，以避免性能问题和潜在的未定义行为。&lt;/strong&gt;&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt; &lt;span class="nd"&gt;#[repr(packed)]&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;PackedExample&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;PackedExample&lt;/code&gt;的对齐值为：1，其实例的总长度为：5。对应的内存布局如下：
 &lt;img src="/uploads/photo/james/3c8d8ffb-7aba-4165-8b48-9bc553b43f6f.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h2 id="总结"&gt;总结&lt;/h2&gt;
&lt;p&gt;本文系统地讨论了结构体相关的内容，主要包括结构体的意义、结构体的定义与实例化以及关联函数与方法等内容。在文章后面，还就结构体的可见性与内存布局等问题，进行了简要地介绍。结构体是构建 Rust 程序的重要构件，希望本文能对正在学习 Rust 的您有好所帮助。&lt;/p&gt;

&lt;p&gt;好了，这可能是结构体中的所有内容了！嗯，我是说可能是。&lt;/p&gt;</description>
      <author>james</author>
      <pubDate>Wed, 27 Nov 2024 03:26:08 +0800</pubDate>
      <link>https://soldev.cn/topics/107</link>
      <guid>https://soldev.cn/topics/107</guid>
    </item>
    <item>
      <title>不以规矩，不能成方圆 —— Rust 王国的物权法</title>
      <description>&lt;blockquote&gt;
&lt;p&gt;孟子曰：离娄之明，公输子之巧，不以规矩，不能成方圆。
 —— 《孟子》离娄章句上篇语&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;所有权与借用机制，明确了内存资源的权属，规范了借用内存资源的秩序，巧妙地解决了内存管理中的三大经典问题，是 Rust 王国的物权法。本文将从核心思想、具体规则以及对规则的验证等方面，对所有权与借用机制进行深入浅出的介绍，旨在让读者能快速的理解所有权与借用的内涵。&lt;/p&gt;
&lt;h2 id="核心思想"&gt;核心思想&lt;/h2&gt;
&lt;p&gt;我们已经知道了一些事实的真相。那就是，栈中的内容会自动实现创建与释放，每调用一个函数都会创建一个函数栈，函数内部的创建的变量在这个栈里有效，当函数返回时，这个函数栈会整体的被弹出，这些变量就变得不可见了，也就等同于被释放了。函数栈的空间范围，就是栈内变量的生命周期，变量仅在其生命周期内有效。我们还知道，对堆的使用，其实都需要通过栈作为中转。分配一个堆上的空间，会在栈上有一个指针类型的变量与之关联，之后对堆内容的读写操作，无一不是通过与之关联的栈上指针变量进行的。&lt;/p&gt;

&lt;p&gt;很显然，栈与堆存在着依赖关系，它们同生不同死，一个是自生自灭，另一个却贪恋红尘、不死不休。说到这里，一个自然而然的想法就是，我们能不能通过栈中的变量来管理堆中内容，能让他们同生共死呢？这种朴素的想法，即使是山野山夫也能想到的，这也是“道法自然”的体现，数据结构中的“树”、“链表”、“队列”等等都是如此。&lt;/p&gt;

&lt;p&gt;Rust 正是基于这种朴素自然的思想，提出了以所有权为基石的一系列方法论，用以回答内存管理中的三个问题的。其核心思想是：&lt;strong&gt;把栈与堆关联起来，通过对栈的自动化先天特性进行赋能，以实现对堆资源管理的自动化。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;为了实现上述核心思想，Rust 设计了具体的方案。那就是，把所有的内存资源“拟物化”，然后比照我们现实社会，制订了《物权法》来管理这些资源。编译器充当大法官的角色，在编译时，对源代码进行检查，如果存在有不合法行为，就拒绝编译。&lt;/p&gt;

&lt;p&gt;该物权法包括两部分内容，一是所有权，二是借用。所有权篇，明确了物的权属问题，即回答了“内存资源的所有者是谁？谁有权处置？”等问题。借用篇，提出了借用的概念（借用者可以通过引用方式借用内存资源），并明确了多个借用者之间、借用者与所有者之间的关系，旨在盘活内存资源、挖掘其资源价值。前者是体，后者为用。&lt;/p&gt;
&lt;h2 id="所有权"&gt;所有权&lt;/h2&gt;
&lt;p&gt;所有权部分相关的主要法条如下：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1.每一个值，都有一个对应的变量作为它的所有者；&lt;/li&gt;
&lt;li&gt;2.在某一时段内，值有且只有一个所有；&lt;/li&gt;
&lt;li&gt;3.当所有者离开自己的作用域时，它持有的值就会被释放掉。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;现在对该法条中的“所有者”与“作用域”以及其相关的概念，作一下解读。&lt;/p&gt;
&lt;h3 id="所有者"&gt;所有者&lt;/h3&gt;
&lt;p&gt;对于栈中的值，其所有者就是自身或者说它对应的变量符号；对于堆中的值，其所有者就是保存在栈上的变量。&lt;/p&gt;

&lt;p&gt;看一张图：&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/james/34d5dab3-84bf-462f-8907-7e493a6f097d.jpg!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h3 id="作用域"&gt;作用域&lt;/h3&gt;
&lt;p&gt;作用域是针对变量而言的，通俗地讲就是它的可见范围。如果变量是可见的，其所代表的资源是存活的；如果变量是不可见的，其所代表的资源就应该被释放。一个变量是否可见，就是看它是否在当前的程序栈中，在栈内的变量就是可见，否则就是不可见的。所以，变量作为域的长度，等于变量所在位置到最内层栈的栈顶长度。事实上，作用域是生命周期的空间化概念。&lt;/p&gt;

&lt;p&gt;比如如下代码：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// 变量 s 还没有声明，所以 s 不可见、不可用&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// 从这里开始， s 可见、可用&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;// 作用域结束， s 不可见、不可用&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="复制与移动"&gt;复制与移动&lt;/h3&gt;
&lt;p&gt;值是资源，变量是所有者。我们的目的是要让值与变量保持一一对应，达到同生共死。当一个变量赋值给别一个变量的时候 (函数的传参也一样），就要考虑到始终保持这种一一对应关系不被打破。对此，就赋值行为，Rust 引入了“复制”与“移动”机制，以对不同的场景作分别处理。&lt;/p&gt;
&lt;h4 id="1. 复制"&gt;1. 复制&lt;/h4&gt;
&lt;p&gt;所谓的复制，是把值按位逐一拷贝一份，然后用一个新的变量（即所有者）与之关联。&lt;/p&gt;

&lt;p&gt;是否采取复制行为，关键是看一个类型是否实现了 &lt;code&gt;Copy&lt;/code&gt; 特征。对于储备在栈上的值，其内存大小是在编译时可知的，比如基本数据类型都默认实现了该特征，所以会采取复制行为。&lt;/p&gt;

&lt;p&gt;这些类型主要有：&lt;/p&gt;
&lt;table class="table table-bordered table-striped"&gt;
&lt;tr&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;描述&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;基本类型&lt;/td&gt;
&lt;td&gt;整数、浮点数、布尔值和字符类型等&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;复合类型&lt;/td&gt;
&lt;td&gt;数组、元组&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;指针类型&lt;/td&gt;
&lt;td&gt;引用、裸指针、函数指针等&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;比如：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 下面的每个值都只有一个所有者，赋值时的行为是复制&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;num1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;num2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;num1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// num2是一个新的所有者，它的值是 num1值的复制品，num1仍然是一个有效的所有者&lt;/span&gt;
&lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;num1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 42,可以通过 num1 使用值&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;arr1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;arr2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;arr1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;p1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;p2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="2. 移动"&gt;2. 移动&lt;/h4&gt;
&lt;p&gt;移动是 Rust 的默认行为，没有实现了&lt;code&gt;Copy&lt;/code&gt;特征的类型变量在赋值（或函数传参）时，都会发生移动行为。&lt;strong&gt;所谓的移动，是指会让值的原变量失效，而新的变量作为值的所有者。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;现在我们来讨论一下为什么要这么设计？
假设有一个栈中的变量&lt;code&gt;a&lt;/code&gt;，是指针类型，它里面所保存的值是某个 &lt;code&gt;堆内存&lt;/code&gt; 中的地址，此时，栈与栈的内容如下图：&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/james/a7dc9eec-f48f-464e-9000-3d9aded9561e.jpg!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;当把这个变量&lt;code&gt;a&lt;/code&gt;赋值给同类型的变量&lt;code&gt;b&lt;/code&gt;会发生什么呢？&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;指针类型&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;堆内存A的起始地址&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;指针类型&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果按照之前的复制行为进行赋值，栈与堆中的内容会如下：&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/james/affe5fd6-8761-42a0-ae37-951f2edaa317.jpg!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;如图所示，此时堆中的值有栈上的两个变量与之关联。这就违背了“一个值只有一个变量作为所有者”的规定，所以这种复制行为对于指针类型的变量在赋值时就不适应。上面讨论的是纯粹的指针类型变量的情况，对于组合类型，如果其中某个分量的类型也是指针这种类型，并且指向堆内存的值，也会出现同样的问题，即一个值将会有多个变量与其关联。出现这种情况的原因是，这种复制其实是&lt;code&gt;浅拷贝&lt;/code&gt;，即仅仅对栈上的内存进行&lt;code&gt;byte to byte&lt;/code&gt;的拷贝。顺便提一下，Rust 也支持深拷贝，提供了一个叫做&lt;code&gt;Clone&lt;/code&gt;的特征，其中有个 &lt;code&gt;clone()&lt;/code&gt;方法会实现深拷贝。但通常深拷贝会消耗更多的性能，所以必须显式调用。&lt;/p&gt;

&lt;p&gt;如果按移动方式进行的话，上面这个例子中，当完成赋值操作后，原变量&lt;code&gt;a&lt;/code&gt; 将失效，不允许再使用。此时，栈与堆中的内容如下：&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/james/7f88b476-97ec-44a8-a09d-0e07df5252ed.jpg!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;对于在编译期无法知道其大小的类型（这种类型要么直接关联有堆内存中的值，要么至少有一个分量关联有堆内存中的值），比如字符串类型，在赋值时会采取移动行为。这些类型主要有：&lt;/p&gt;
&lt;table class="table table-bordered table-striped"&gt;
&lt;tr&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;描述&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;字符串类型&lt;/td&gt;
&lt;td&gt;str, 本质上是一个 u8 类型的数据序列，实际中经常使用的形式：&amp;amp;str 和 String&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;切片类型&lt;/td&gt;
&lt;td&gt;[T], 它代表类型为 T 的元素组成的数据序列：实际中经常使用的形式：Vec&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;trait object&lt;/td&gt;
&lt;td&gt;trait object 的大小只有在运行时才能确定&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;&lt;h4 id="3. 自动释放"&gt;3. 自动释放&lt;/h4&gt;
&lt;p&gt;通过上面的分析，可以看出 &lt;code&gt;所有者&lt;/code&gt;、 &lt;code&gt;复制与移动&lt;/code&gt; 机制，保证了法条中第一和第二条的规定，但还不能保证第三条的要求。变量在&lt;code&gt;作用域&lt;/code&gt; 结束后，将变得不可访问（类似于释放），但如果这个变量是指针类型，还应该让其所指向的堆内容也一同释放。为了实现这一要求，Rust 约定，一个类型如果实现了&lt;code&gt;Drop&lt;/code&gt;特征（其中有一个&lt;code&gt;drop&lt;/code&gt;方法），该类型的变量离开作用域之前，编译器会插入调用&lt;code&gt;drop&lt;/code&gt;方法的代码，用以负责处理释放堆内容等相关善后操作。&lt;/p&gt;

&lt;p&gt;这样，当栈变量释放的同时，与之关联的堆空间也得到了释放，也保证了法条的第三条的规定。这也是体现所有权的核心思想：把栈与堆关联起来，通过对栈的自动化先天特性进行赋能，以实现对堆资源管理的自动化。&lt;/p&gt;
&lt;h3 id="验证所有权规则"&gt;验证所有权规则&lt;/h3&gt;
&lt;p&gt;所有权相关的内容已经讨论完了，现在我们来验证下。下面我们分别使用编译期可知大小的基本类型&lt;code&gt;i32&lt;/code&gt;与编译期未知大小的动态类型&lt;code&gt;String&lt;/code&gt;进行验证。字符串类型&lt;code&gt;String&lt;/code&gt;内部有一个指针类型的变量，指向堆上实际存放字符串的空间，程序在运行的时候，可能通过操作这块堆内存实现字符串内容的动态增减。&lt;/p&gt;

&lt;p&gt;现在，我们开始验证吧。看看，这些类型能不能满足所有权机制的要求。&lt;/p&gt;
&lt;h4 id="1. 验证固定尺寸的类型"&gt;1. 验证固定尺寸的类型&lt;/h4&gt;&lt;h5 id="作用域"&gt;作用域&lt;/h5&gt;&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

   &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// b 的作用域是内部的花括号内&lt;/span&gt;
      &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;

   &lt;span class="c1"&gt;// 此处，b 已经离开作用域，固不可见、不可访问&lt;/span&gt;
   &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;以上代码无法通过编译，会报：&lt;code&gt;error[E0425]: cannot find value&lt;/code&gt;b&lt;code&gt;in this scope&lt;/code&gt;错误，说明变量仅在作用域内生效。&lt;/p&gt;
&lt;h5 id="赋值"&gt;赋值&lt;/h5&gt;&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 发生复制行为&lt;/span&gt;
   &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{},{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;以上代码能通过编译，并输出 &lt;code&gt;5,5&lt;/code&gt;&lt;/p&gt;
&lt;h5 id="函数传参"&gt;函数传参&lt;/h5&gt;&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;func1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="nf"&gt;func1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;//传参时，发生复制行为&lt;/span&gt;

   &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 传参后，原变量仍然有效&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;以上代码，能通过编译，运行后会打印出变量&lt;code&gt;a&lt;/code&gt;的值：5。&lt;/p&gt;
&lt;h4 id="2. 验证动态尺寸的类型"&gt;2. 验证动态尺寸的类型&lt;/h4&gt;&lt;h5 id="作用域"&gt;作用域&lt;/h5&gt;&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// s1 的作用域是内部的花括号内&lt;/span&gt;
      &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;s1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"abc"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;s2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 此处，s1 已经离开作用域，固不可见、不可访问&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;以上代码无法通过编译，会报：&lt;code&gt;error[E0425]: cannot find value&lt;/code&gt;s1&lt;code&gt;in this scope&lt;/code&gt; 错误，说明变量仅在作用域内生效。&lt;/p&gt;
&lt;h5 id="赋值"&gt;赋值&lt;/h5&gt;&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;s1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hello"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

   &lt;span class="c1"&gt;// 因为，String这种动态类型&lt;/span&gt;
   &lt;span class="c1"&gt;// 没有实现`Copy`特征，所以赋值时发生的是移动行为，所有权转移至新的变量&lt;/span&gt;
   &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;s2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 

   &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;以上代码无法通过编译，会报：&lt;code&gt;error[E0382]: borrow of moved value:&lt;/code&gt;s1`` 错误，说明所有权发生转移，原变量失效。&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;6  |    &lt;span class="nb"&gt;let &lt;/span&gt;s1 &lt;span class="o"&gt;=&lt;/span&gt; String::from&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"hello"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   |        &lt;span class="nt"&gt;--&lt;/span&gt; move occurs because &lt;span class="sb"&gt;`&lt;/span&gt;s1&lt;span class="sb"&gt;`&lt;/span&gt; has &lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="sb"&gt;`&lt;/span&gt;String&lt;span class="sb"&gt;`&lt;/span&gt;, which does not implement the &lt;span class="sb"&gt;`&lt;/span&gt;Copy&lt;span class="sb"&gt;`&lt;/span&gt; trait
...
10 |    &lt;span class="nb"&gt;let &lt;/span&gt;s2 &lt;span class="o"&gt;=&lt;/span&gt; s1&lt;span class="p"&gt;;&lt;/span&gt; 
   |             &lt;span class="nt"&gt;--&lt;/span&gt; value moved here
11 |
12 |    println!&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"{}"&lt;/span&gt;, s1&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   |                   ^^ value borrowed here after move
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;以上代码之所以会无法通过编译的原因是，&lt;code&gt;String&lt;/code&gt;这种类型由于没有实现&lt;code&gt;Copy&lt;/code&gt;特征，在赋值的时候进行的移动行为。移动作为会导致值所关联的原变量失效，值的所有得变成新的变量。这个行为如下图所示：
&lt;img src="/uploads/photo/james/339a6d63-a207-428b-9468-22bd041c4930.jpg!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;如果不是进行移动，而是复制，会是下图的效果：
&lt;img src="/uploads/photo/james/226c46e4-b3d7-44f9-9327-92d7e28d5056.jpg!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;如果把&lt;code&gt;let s2 = s1&lt;/code&gt; 改成&lt;code&gt;let s2 = s1.clone()&lt;/code&gt;，实现的深拷贝，其效果如下：
&lt;img src="/uploads/photo/james/22c75dea-a625-41be-a995-4c93d80f055c.jpg!large" title="" alt=""&gt;
&lt;img src="/uploads/photo/james/8f3b6755-e9f0-4808-9613-dfec84ca8ebf.jpg!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h5 id="函数传参"&gt;函数传参&lt;/h5&gt;&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;func1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;s1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"abc"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="nf"&gt;func1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 发生移动行为，所有权转移到函数中，原变量s1不可用&lt;/span&gt;

   &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;s1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// s1 在此处不可用&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;函数传参的规则跟赋值类似，函数调用时会传参，传参数的过程等同于赋值。上面代码无法通过编译，会报：&lt;code&gt;error[E0382]: borrow of moved value:&lt;/code&gt;s1`&lt;code&gt;错误，说明所有权转移至&lt;/code&gt;func1&lt;code&gt;函数体内，原变量&lt;/code&gt;s1`失效。&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;error[E0382]: borrow of moved value: &lt;span class="sb"&gt;`&lt;/span&gt;s1&lt;span class="sb"&gt;`&lt;/span&gt;
 &lt;span class="nt"&gt;--&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; src/main.rs:9:18
  |
6 |    &lt;span class="nb"&gt;let &lt;/span&gt;s1 &lt;span class="o"&gt;=&lt;/span&gt; String::from&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"abc"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  |        &lt;span class="nt"&gt;--&lt;/span&gt; move occurs because &lt;span class="sb"&gt;`&lt;/span&gt;s1&lt;span class="sb"&gt;`&lt;/span&gt; has &lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="sb"&gt;`&lt;/span&gt;String&lt;span class="sb"&gt;`&lt;/span&gt;, which does not implement the &lt;span class="sb"&gt;`&lt;/span&gt;Copy&lt;span class="sb"&gt;`&lt;/span&gt; trait
7 |    func1&lt;span class="o"&gt;(&lt;/span&gt;s1&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; // 发生移动行为，所有权转移到函数中，原变量s1不可用                    ...
  |          &lt;span class="nt"&gt;--&lt;/span&gt; value moved here
8 |
9 |    println!&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"{}"&lt;/span&gt;,s1&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; // s1 在此处不可用
  |                  ^^ value borrowed here after move
&lt;/code&gt;&lt;/pre&gt;&lt;h5 id="函数返回"&gt;函数返回&lt;/h5&gt;
&lt;p&gt;值的所有权，也可以从函数内部返回到调用方。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fn main() {
  let s1 = gives_ownership();         // gives_ownership 将返回值
                                      // 移给 s1

  let s2 = String::from("hello");     // s2 进入作用域

  let s3 = takes_and_gives_back(s2);  // s2 被移动到
                                      // takes_and_gives_back 中,
                                      // 它也将返回值移给 s3
} // 这里, s3 移出作用域并被丢弃。s2 也移出作用域，但已被移走，
  // 所以什么也不会发生。s1 移出作用域并被丢弃

fn gives_ownership() -&amp;gt; String {           // gives_ownership 将返回值移动给
                                           // 调用它的函数

  let some_string = String::from("yours"); // some_string 进入作用域

  some_string                              // 返回 some_string 并移出给调用的函数
}

// takes_and_gives_back 将传入字符串并返回该值
fn takes_and_gives_back(a_string: String) -&amp;gt; String { // a_string 进入作用域

  a_string  // 返回 a_string 并移出给调用的函数
}
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="3. 验证自动释放"&gt;3. 验证自动释放&lt;/h4&gt;
&lt;p&gt;为了验证当变量离开作用域时，会不会调用&lt;code&gt;Drop&lt;/code&gt;特征中的&lt;code&gt;drop&lt;/code&gt;方法以完成诸如释内存等清尾工作。我们需要自己定义一个类型，并为这个类型实现&lt;code&gt;Drop&lt;/code&gt;特征。我们还没有学习如何自定义类型，也不知道特征是怎么事。我在这里简单地介绍一下，特征类似于别的语言中的接口，它定义一组行为，具体就是指一组方法，用于描述这个特征所具备的能力。这些方法，通常都是没有实现的，仅仅只是给出方法的签名，如果某个类型实现了某个特征中的这些方法，就称这个类型具备了某个特征。&lt;/p&gt;

&lt;p&gt;首先，我们定义一个类型：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nf"&gt;Wrapper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上面代码的意思是，我们定义了结构体类型&lt;code&gt;Wrapper&lt;/code&gt;，它包含了一个&lt;code&gt;i32&lt;/code&gt;类型的成员。但需要主要的是，&lt;code&gt;Wrapper&lt;/code&gt;类型实例虽然在内存中的布局与&lt;code&gt;i32&lt;/code&gt;一样，但它的实例是不能与&lt;code&gt;i32&lt;/code&gt;实例进行互相赋值。
第二步，就是为&lt;code&gt;Wrapper&lt;/code&gt;类型实现&lt;code&gt;Drop&lt;/code&gt;特征，因为该特征已经有标准库定义好了，它只有一个方法 drop，所以我们实现这个方法就可以了。&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="nb"&gt;Drop&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;Wrapper&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;drop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Wrapper类型的实例正在被释放..."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;至此，我们自定义了一个类型，并且为该类型实现了&lt;code&gt;Drop&lt;/code&gt;特征，接下来我们看看，当这个类型实例的变量（所有者）离开作用域时，会发生什么？&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nf"&gt;Wrapper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="nb"&gt;Drop&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;Wrapper&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;drop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Wrapper类型的实例正在被释放..."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;wrapper1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Wrapper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;wrapper2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;wrapper1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 所有权转移到 wrapper2, 原所有者失效&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;//  wrapper2先离开作用域，由于它是值的所有者，所以还会调用drop()方法&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;执行&lt;code&gt;cargo run&lt;/code&gt;运行上面的程序，终端上会输出：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Wrapper类型的实例正在被释放...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;以上代码中，所有权由&lt;code&gt;wrapper1&lt;/code&gt;转移到&lt;code&gt;wrapper2&lt;/code&gt;，当&lt;code&gt;wrapper2&lt;/code&gt;离开作用域时会调用&lt;code&gt;drop()&lt;/code&gt;方法释放资源，由于&lt;code&gt;wrapper1&lt;/code&gt;已经失效，所以没有调用&lt;code&gt;drop()&lt;/code&gt;方法，保证了没有重复释放问题。&lt;/p&gt;
&lt;h2 id="借用"&gt;借用&lt;/h2&gt;
&lt;p&gt;所有权机制，把内存资源限制在其所有者内，要对已有内存资源的使用意味着所有权的转移，这就限制了内存作为资源的使用价值。为了盘活内存资源价值，Rust 引入了借用机制。&lt;/p&gt;

&lt;p&gt;借用是目的，引用是手段。其实质是，通过引用的方式，借用原本没有所有权的值。&lt;/p&gt;
&lt;h3 id="引用"&gt;引用&lt;/h3&gt;
&lt;p&gt;引用，是 Rust 指针类型家族中的重要成员之一（另一个类型是祼指针，祼指针用于 Unsafe 场景，引用用于 Safe 场景），分为&lt;code&gt;不可变引用(&amp;amp;T)&lt;/code&gt; 与 &lt;code&gt;可变引用(&amp;amp;mut T)&lt;/code&gt;。引用不持有值的所有权，当引用离开其作用域时，它所指向的值不会被丢失。这也就是为什么把引用也叫作借用的原因。之所以有&lt;code&gt;不变&lt;/code&gt;与&lt;code&gt;不可变&lt;/code&gt;之别，主要是区分引用对被引用本身的使用权限，前者只能读，后者还可以修改。&lt;/p&gt;

&lt;p&gt;与引用机制相关的主要法条如下：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;在任何一段&lt;strong&gt;给定的时间里&lt;/strong&gt;，你要么只能拥有一个可变引用，要么只能拥有任意数量的不可变引用。&lt;/li&gt;
&lt;li&gt;引用总是有效。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="不可变引用"&gt;不可变引用&lt;/h4&gt;
&lt;p&gt;不可变引用，用&lt;code&gt;&amp;amp;T&lt;/code&gt;表示，其中&lt;code&gt;T&lt;/code&gt;表示具体的类型，通过不可变引用无法去修改被引用本体的内容。&lt;/p&gt;

&lt;p&gt;比如：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// 定义了一个引用类型ref_a, 它保存的内容是变量a的内存地址，&amp;amp;表示取地址，在这里表示取变量a在内存中的地址。&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;ref_a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以通过&lt;code&gt;ref_a&lt;/code&gt;来访问它所引用的本身 a，这个过程叫作&lt;code&gt;解引用&lt;/code&gt;，解引用是通过&lt;code&gt;*&lt;/code&gt;来完成的。
比如：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 对引用类型的变量ref_a进行解引用，具体过程是先找到ref_a中的内容（变量a的地址），&lt;/span&gt;
&lt;span class="c1"&gt;// 然后再根据该地址去找到变量a 中的内容。&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ref_a&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;引用跟其他指针类型一样，其内容都是另一个值的内存地址。但一个重要的区别是，引用必须要有被引用的本身，否则就失去了引用的本身意义。也就是说，在对引用进行解引用之前，引用必须要被初始化，即其中的内容必须是一个变量的内存地址，且这个变量是有效的，这就是法条中“引用总是有效”的意思。这条规则适用于所有引用，包括不可变与可变引用。&lt;/p&gt;

&lt;p&gt;看一段代码：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;ref_some&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;some_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ref_some&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;以上代码无法通过编译，会报类似如下的错误信息：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;error[E0381]: used binding &lt;span class="sb"&gt;`&lt;/span&gt;ref_some&lt;span class="sb"&gt;`&lt;/span&gt; isn&lt;span class="s1"&gt;'t initialized
 --&amp;gt; src/main.rs:4:22
  |
2 |     let ref_some: &amp;amp;i32;
  |         -------- binding declared here but left uninitialized
3 |
4 |     let some_value = *ref_some;
  |                      ^^^^^^^^^ `*ref_some` used here but it isn'&lt;/span&gt;t initialized
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;由于引用不占用值的所有权，传递引用不会导致被引用资源的所有权转移，这在诸如“用一个变量调用一个函数后，继续使用原变量”这种需要多种使用资源的场景特别有用。比如：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;s1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hello"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;len&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;calculate_length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;s1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"The length of '{}' is {}."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;calculate_length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;usize&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="nf"&gt;.len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上面代码，通过引用&lt;code&gt;&amp;amp;s1&lt;/code&gt;调用&lt;code&gt;calculate_length()&lt;/code&gt;计算出字符串&lt;code&gt;s1&lt;/code&gt;的长度后，由于没有发生所有权的转移，所以可以继续使用&lt;code&gt;s1&lt;/code&gt;。这就把借用的内涵体现的淋漓尽致。&lt;/p&gt;

&lt;p&gt;如果我们要通过不可变引用来改变被引用本身的内容是不被允许的，比如：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hello"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nf"&gt;change&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;change&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;some_string&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;some_string&lt;/span&gt;&lt;span class="nf"&gt;.push_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;", world"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上面代码将无法通过编译，会报类似如下的错误：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;error[E0596]: cannot borrow &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;some_string&lt;span class="sb"&gt;`&lt;/span&gt; as mutable, as it is behind a &lt;span class="sb"&gt;`&lt;/span&gt;&amp;amp;&lt;span class="sb"&gt;`&lt;/span&gt; reference
 &lt;span class="nt"&gt;--&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; src/main.rs:8:5
  |
8 |     some_string.push_str&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;", world"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  |     ^^^^^^^^^^^ &lt;span class="sb"&gt;`&lt;/span&gt;some_string&lt;span class="sb"&gt;`&lt;/span&gt; is a &lt;span class="sb"&gt;`&lt;/span&gt;&amp;amp;&lt;span class="sb"&gt;`&lt;/span&gt; reference, so the data it refers to cannot be borrowed as mutable
  |
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;其原因是，我们不能通过不可变引用&lt;code&gt;&amp;amp;s&lt;/code&gt;调用&lt;code&gt;push_str&lt;/code&gt;方法改变字符串的内容。&lt;/p&gt;
&lt;h4 id="可变引用"&gt;可变引用&lt;/h4&gt;
&lt;p&gt;可变引用，用&lt;code&gt;&amp;amp;mut T&lt;/code&gt;表示，其中&lt;code&gt;T&lt;/code&gt;表示具体的类型，关键字&lt;code&gt;mut&lt;/code&gt;强调可变，通过可变引用可以修改被引用本体的内容。&lt;/p&gt;

&lt;p&gt;把上面代码稍做如下修改，即可通过编译。修改后的代码：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hello"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nf"&gt;change&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;change&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;some_string&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;some_string&lt;/span&gt;&lt;span class="nf"&gt;.push_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;", world"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;下面代码，是定义可变引用 (&amp;amp;T)，并进行解引用与改变内容的示例：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt;  &lt;span class="k"&gt;mut&lt;/span&gt;  &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 必须标注为mut,否则无法被可变的引用&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;ref_a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ref_a&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"b = {b}"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ref_a&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"After changed: a= {a}"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;另外一点要注意的是，一个变量要允许被可变地引用，其本身必须是能被修改的，这是显然的，一个不允许被修改的变量，当然通过引用也不能被修改，因为毕竟引用只是本体的一个别名罢了。&lt;/p&gt;
&lt;h3 id="验证引用的规则"&gt;验证引用的规则&lt;/h3&gt;
&lt;p&gt;在验证引用的规则之前，我想再重复一次这个规则。规则一是，在任何一段给定的时间里，你要么只能拥有一个可变引用，要么只能拥有任意数量的不可变引用；规则二是，引用总是有效的。规则一，我们在前面已经验证过了，现在我们来验证规则二。&lt;/p&gt;
&lt;h4 id="可以拥有任意数量的不可变引用"&gt;可以拥有任意数量的不可变引用&lt;/h4&gt;&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"the web3"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;r1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;r2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;r1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;r3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;以上代码演示了可以拥有任意数量的不可变引用，可以通过编译。&lt;/p&gt;
&lt;h4 id="在任何一段给定的时间里，只能拥有一个可变引用"&gt;在任何一段给定的时间里，只能拥有一个可变引用&lt;/h4&gt;&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt;  &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"the web3"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;r1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;r1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;r2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;r2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;//println!("{}", *r1); // 放开这行代码，无法通过编译&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;以上代码，对字符串&lt;code&gt;s&lt;/code&gt;有两个借用&lt;code&gt;r1&lt;/code&gt;和&lt;code&gt;r2&lt;/code&gt;，应该是违背了这个规则。但是却能正常通过编译！！这到底是哪里出问题呢？难道《物权法》的规则失效了？&lt;/p&gt;

&lt;p&gt;其实不然，是我们没有读懂“一段给定的时间里”的内涵。因为我们的代码是顺序执行的，它所强调的给定时间主要是强调是引用定义到该引用使用这一段时间，比如上面的引用&lt;code&gt;r1&lt;/code&gt;在定义到使用 (通过引用打印）的这段时间内，没有其他可变引用引用本体&lt;code&gt;s&lt;/code&gt;，而使用完&lt;code&gt;r1&lt;/code&gt;后，后面再没有通过&lt;code&gt;r1&lt;/code&gt;就访问或修改本体，虽然&lt;code&gt;r1&lt;/code&gt;的作用域持续到函数结束处，但编译器认为它作为引用的使命就已经结束了，而当使用&lt;code&gt;r2&lt;/code&gt;的时候，从定义&lt;code&gt;r2&lt;/code&gt;到使用&lt;code&gt;r2&lt;/code&gt;这个时间段内，也只有&lt;code&gt;r2&lt;/code&gt;引用到本体。可见，这是符合规则要求的，所以能正常通过编译。&lt;/p&gt;
&lt;h4 id="不可变引用与可变引用不能同时存在"&gt;不可变引用与可变引用不能同时存在&lt;/h4&gt;&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt;  &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"the web3"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;r1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;r1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;r2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;r2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;r3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;r3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;//println!("{}", *r2); // 放开这行代码，无法通过编译&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;以代码也是可以正常通过编译，其原因跟上面的一样，当使用&lt;code&gt;r3&lt;/code&gt;可变引用时，前面的&lt;code&gt;r1&lt;/code&gt;、&lt;code&gt;r2&lt;/code&gt;作用引用的使命已经结束，此时此刻，对于本体&lt;code&gt;s&lt;/code&gt;而言，它只有一个可变引用&lt;code&gt;r3&lt;/code&gt;，没有不可变引用。&lt;/p&gt;

&lt;p&gt;如果放开最一行代码，将无法通过编译，因为在&lt;code&gt;r3&lt;/code&gt;的时候，对于本体&lt;code&gt;s&lt;/code&gt;而言，它有一个可变引用&lt;code&gt;r3&lt;/code&gt;，还有一个不可变引用&lt;code&gt;r2&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;对于同一个本身的引用中，“只能有一个可变引用”以及“可变引用与不可变引用不能共存”的判断，有一个非常实用的标准，那就是：如果定义引用处至最后使用引用处之间，包含有另外的对于同一本体的引用的定义或使用，就违背了规则，否则符合规则。&lt;/p&gt;

&lt;p&gt;如果套用数学的术语，我们可以尝试作如下定义：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;设 o 为本体，对 o 的某个不可变引用 x 的定义处记为：&lt;code&gt;Rs_o1_x&lt;/code&gt;，最后一个使用处记为：&lt;code&gt;Re_o1_x&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;设 o 为本体，对 o 的可变引用 x 的定义处记为：&lt;code&gt;Rs_o3_x&lt;/code&gt;，最后一个使用处记为：&lt;code&gt;Re_o3_x&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;对于给定的不可变引用 i，在定义处与最后使用处的区间&lt;code&gt;(Rs_o1_i,Re_o1_i)&lt;/code&gt;内，如果存在任意&lt;code&gt;Rs_o3_x&lt;/code&gt; 或 &lt;code&gt;Re_o3_x&lt;/code&gt; （其中 x&amp;lt;&amp;gt;i) ，则违背了规则；&lt;/li&gt;
&lt;li&gt;&lt;p&gt;对于给定的可变引用 i，在定义处与最后使用处的区间&lt;code&gt;(Rs_o3_i,Re_o3_i)&lt;/code&gt;内，如果存在任意&lt;code&gt;Rs_o1_x/Rs_o3_x&lt;/code&gt; 或 &lt;code&gt;Re_o1_x/Re_o3_x&lt;/code&gt; （其中 x&amp;lt;&amp;gt;i)，则违背了规则；&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;其他情况，符合借用法的规则。&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;对不起，我承认我已经小题大作了！其实把定义处与最后使用处画一条线连起来，就明白了，线条没有交叉和包含就可以了。&lt;/p&gt;
&lt;h3 id="切片"&gt;切片&lt;/h3&gt;
&lt;p&gt;切片是引用类型的一种，它不同于普通引用把被引用本体当成一个整体使用，切片只对被引用本体的部分内容进行引用。这就是切片类型的实质！切片通常是对诸如数组、字符串等这种连续数据中的部分序列进行引用。&lt;/p&gt;

&lt;p&gt;由于切片也是一种引用，所以引用类型所具备的所有特性、对引用约束的机制以及引用的使用场景，同样适合于切片类型。&lt;/p&gt;
&lt;h4 id="字符串字面量的切片"&gt;字符串字面量的切片&lt;/h4&gt;&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"hello world"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上面代码中的"hello world"就是字符串字面量，编译后会被直接编译到二进制程序中，程序加载内存后，会被存储在静态区，静态区中的内容是不能修改的。"hello world" 的类型是&lt;code&gt;str&lt;/code&gt;，它是一个虚无的类型，它表示在静态区中的数据序列，在编译时无法这个序列有多长，因为里面的具体内容及长度取决于程序中各种字符串字面量定义情况。这有点类似于镜中花、水中月，只可意喻、不可言传，更不能直接拿来使用。
要使用&lt;code&gt;str&lt;/code&gt;，就必须通过它的引用&lt;code&gt;&amp;amp;str&lt;/code&gt;，但由于静态区保存有很多内容，不能把整个内容当成一个整体引用来使用，于是就需要明确指出引用从何处开始，至何处结束，这也体现了切片是引用部分内容的实际意义。可见，引用也是解决无法使用编译期未知大小类型的万能法宝！&lt;/p&gt;

&lt;p&gt;上面代码中的&lt;code&gt;s&lt;/code&gt;就是一个&lt;code&gt;&amp;amp;str&lt;/code&gt;类型，表示对静态内存区中某一段内容的引用。&lt;/p&gt;

&lt;p&gt;切片类型通常是一个保存栈上的结构体，其中一个字段用来标识引用的起始位置，另一个字段用来标识要引用数据的长度。&lt;/p&gt;
&lt;h4 id="字符串切片"&gt;字符串切片&lt;/h4&gt;
&lt;p&gt;字符串切片是对字符串&lt;code&gt;String&lt;/code&gt;中保存在堆内存中的实际数据感兴趣，引用其中某一段内容。
比如：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hello world"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;hello&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;world&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上面的&lt;code&gt;hello&lt;/code&gt;的类型是&lt;code&gt;&amp;amp;str&lt;/code&gt;，表示一个对 String 类型的切片，具体意思是，引用字符串&lt;code&gt;s&lt;/code&gt;在堆内存中的数据序列中从第一个位置开始至第五个位置结束这一段内容，&lt;code&gt;[0..5]&lt;/code&gt;中&lt;code&gt;0&lt;/code&gt;表示起始位置的索引值，&lt;code&gt;5&lt;/code&gt;表示切片终止位置的下一个位置。&lt;/p&gt;

&lt;p&gt;上面代码中的变量&lt;code&gt;world&lt;/code&gt;在内存的示意图：
&lt;img src="/uploads/photo/james/fd78b5f4-89af-4db3-9c31-3bfd097b795a.jpg!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h4 id="数组切片"&gt;数组切片&lt;/h4&gt;
&lt;p&gt;数组保存着同类型的连续的元素，也可以通过切片来引用部分元素序列。&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;slice_for_arr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;slice_for_arr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上面代码中&lt;code&gt;slice_for_arr&lt;/code&gt; 就是对数组&lt;code&gt;arr&lt;/code&gt;的切片，它引用从第 2 个元素开始至第 4 个元素结束的序列（[2,3,4])。&lt;/p&gt;

&lt;p&gt;这里有几个概念要作一下解释：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;[T;n]&lt;/code&gt; 表示数组，比如&lt;code&gt;[i32,3]&lt;/code&gt;是一个长度为 3、内部的元素的类型为 i32 的数组；&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;[T]&lt;/code&gt; 是一个虚无的概念，程序中无法直接使用，表示某个&lt;code&gt;类型T&lt;/code&gt;的数据序列，类似于&lt;code&gt;str&lt;/code&gt;编译期无法知道其大小&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;amp;[T]&lt;/code&gt; 是对&lt;code&gt;[T]&lt;/code&gt;的切片，表示对&lt;code&gt;类型T&lt;/code&gt;的数据序列的某个区段范围内的数据的引用，类似于&lt;code&gt;&amp;amp;str&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="悬垂引用"&gt;悬垂引用&lt;/h3&gt;
&lt;p&gt;所有权的一物一所有者机制，解决了内存泄露问题。借用规则的第一条：“在任何一段给定的时间里，要么只能拥有一个可变引用，要么只能拥有任意数量的不可变引用”，解决了数据竞争问题。&lt;/p&gt;

&lt;p&gt;对于“引用总是有效的”之第二条规则，我们已经验证过编译器会确保在使用引用之前必须初始化，否则无法通过编译。这也似乎解决了悬垂指针问题。&lt;/p&gt;

&lt;p&gt;但事实是，编译器没有那么聪明，有时它无法确定一个引用是不是有效的！&lt;/p&gt;

&lt;p&gt;对于一些简单的问题，编译器会明确给出结论，比如：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;ref_i32&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;ref_i32&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ref_i32&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上面代码无法通过编译，会认为是悬垂引用。又比如：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;reference_to_nothing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;dangle&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;dangle&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hello"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上面代码也无法通过编译，同样会认为是悬垂引用，并给出了缺少生命周期参数标的错误。具体的错误信息如下：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;error[E0106]: missing lifetime specifier
 &lt;span class="nt"&gt;--&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; src/main.rs:5:16
  |
5 | fn dangle&lt;span class="o"&gt;()&lt;/span&gt; -&amp;gt; &amp;amp;String &lt;span class="o"&gt;{&lt;/span&gt;
  |                ^ expected named lifetime parameter
  |
  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;help&lt;/span&gt;: this &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="s1"&gt;'s return type contains a borrowed value, but there is no value for it to be borrowed from
help: consider using the `'&lt;/span&gt;static&lt;span class="sb"&gt;`&lt;/span&gt; lifetime, but this is uncommon unless you&lt;span class="s1"&gt;'re returning a borrowed value from a `const` or a `static`
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但对于一些复杂的场景，编译器没有那么智能，它没有办法精准地跟踪一个引用与被引用本体的关系以及两者的生命周期的长短，也就没有办法确保被引用本体是不是比引用活得更长。尤其是在存在引用类型字段的结构体中，且结构体内方法又有引用类型的参数和引用类型的返回值时，对编译器来说，这种不确定性就更为明显。&lt;/p&gt;

&lt;p&gt;基于此，Rust 提出了生命周期与标注机制，把变量的生命周期进行量化，然后让程序员去对引用进行显式的标注，用以向编译器指明函数中各种传入引用与传出引用之间、引用与被引用本体之间的关系，进而让编译去决定是否符合借用规则。&lt;/p&gt;

&lt;p&gt;限于篇幅，不能就生命周期标注的内容作更多的介绍，后续会另开专文解读。&lt;/p&gt;
&lt;h2 id="总结"&gt;总结&lt;/h2&gt;
&lt;p&gt;所有权与借用是 Rust 语言的基石，本文以通俗易懂的言语，就所有权与借用相关的内容进行了全面系统地介绍。
但愿，你能懂我。&lt;/p&gt;</description>
      <author>james</author>
      <pubDate>Wed, 12 Jun 2024 20:00:53 +0800</pubDate>
      <link>https://soldev.cn/topics/29</link>
      <guid>https://soldev.cn/topics/29</guid>
    </item>
    <item>
      <title>欲知山中事，须问打柴人 —— 程序与内存的那点事儿</title>
      <description>&lt;blockquote&gt;
&lt;p&gt;近水识鱼性，近山识鸟音。
欲知山中事，须问打柴人。
—— 《增广贤文》&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;所有权，是 Rust 中的一个重要语言特性，也是其他特性的基石。能否理解所有权的内涵，直接影响 Rust 后续学习的进展。所有权涉及诸如内存管理等其他相关概念，了解这些相关的概念，将有利于加深对所有权概念的理解。所以有必要就相关内容作一个科普性介绍，于是就有了该文。&lt;/p&gt;

&lt;p&gt;当然，这些属于《操作系统》中的知识，本文只是做一个简要的介绍，可能有不准确甚至错误之处，但应该不影响我们的目的 —— 掌握所有权！&lt;/p&gt;
&lt;h2 id="1. 程序的生死轮回"&gt;1. 程序的生死轮回&lt;/h2&gt;&lt;h3 id="1-1. 生成可执行程序"&gt;1-1. 生成可执行程序&lt;/h3&gt;
&lt;p&gt;用计算机程序语言编写的源代码，通过编译器的编译、汇编、链接，生成某个特定目标机器上的可执行程序。这个可执行程序里，存放的都是“二进制”的内容，人类无法识别，专供 CPU 阅读。不同的目标系统上的可执行程序的格式也不一样，比如&lt;code&gt;Windows&lt;/code&gt;下是&lt;code&gt;PE&lt;/code&gt;格式，&lt;code&gt;Mac Os&lt;/code&gt; 下是&lt;code&gt;Mach O&lt;/code&gt;格式，&lt;code&gt;Linux&lt;/code&gt;下是&lt;code&gt;ELF&lt;/code&gt;格式，等等。
这一步，就是无中生有。在物理层面，实实在在地生成了一个文件，只是它还没有生命。&lt;/p&gt;
&lt;h3 id="1-2. 加载到内存"&gt;1-2. 加载到内存&lt;/h3&gt;
&lt;p&gt;当用户运行一个程序时（比如点击桌面上的&lt;code&gt;WeiChat&lt;/code&gt;图标），会呼叫操作系统中一个叫做&lt;code&gt;加载器&lt;/code&gt;的神仙，他会把欲执行程序进行乾坤大挪移，即按照一定的规则把它搬送到内存中合适的位置。这还不够，还需做一系列的准备工作，比如加载程序所依赖的动态库等等。然后会把控制权移交给被加载的程序。这个时候，程序变变身成进程，程序就有了生命。&lt;/p&gt;
&lt;h3 id="1-3. 开始运行"&gt;1-3. 开始运行&lt;/h3&gt;
&lt;p&gt;被加载程序接管控制权后，会从&lt;code&gt;main&lt;/code&gt;开始执行代码，我们的程序就正式开始运行了。程序的运行，其实就是&lt;code&gt;CPU&lt;/code&gt;反复地读写内存的过程。CPU 根据&lt;code&gt;指令寄存器&lt;/code&gt;的内容，从内存中的&lt;code&gt;代码段&lt;/code&gt;中读取具体的指令，然后执行指令，在指令的执行过程中会对内存进行各种读写操作。就这样周而复始、生生不息，直到程序运行完毕或者用户显式关闭。当然，运行过程中遇到了异常也会被强行中断。&lt;/p&gt;
&lt;h3 id="1-4. 打个比方"&gt;1-4. 打个比方&lt;/h3&gt;
&lt;p&gt;上面的这个过程，我想用厨师做菜的过程来来尝试做进一步的解释，虽然不是太恰当，但愿能有所帮助。&lt;/p&gt;

&lt;p&gt;厨房里有一个工作长板，长板上面摆放一些碗，碗里面放有准备好的各种小食材和油盐酱醋等调料等。厨房隔壁有一间很大的储藏间，用来存放各种食材等源材料。假设每一道菜肴都会有两张纸条，一张用以说明制作该菜肴的工艺流程，指导厨师应该怎么做，比如第一步拿什么菜，第二步洗菜，第三步是切菜，等等；别一张，用以说明所需食材的位置，有的就放在厨房的碗中，有的放在储藏间的某层架子上。另外，为厨师配有一个伙计，助手听从厨师的使唤，在储藏间与厨房之间跑腿。&lt;/p&gt;

&lt;p&gt;炒菜时，厨师先会拿到该道菜的两条纸条，按照纸条上的流程一步一步的操作。需要食材时，按照纸条去找。如果食材放在厨房内部的，他自己去拿，如果是放在储藏间，就使唤伙计去跑脚。&lt;/p&gt;

&lt;p&gt;这里的厨师就 CPU，厨房长板上的碗就是内存中的栈，储藏间就是内存中的堆。纸条本身就是可执行程序，当厨师拿到纸条的时候，就代表程序运行起来了。&lt;/p&gt;
&lt;h2 id="2. 内存管理"&gt;2. 内存管理&lt;/h2&gt;
&lt;p&gt;程序加载到内存后，操作系统会为其创建一个对应的进程，并以进程为单位在内存中分配了独立的进程空间。这是一个封闭的空间，该程序中的指令无法访问进程空间外的其他内存空间。进程空间大致分为&lt;code&gt;代码区&lt;/code&gt;、&lt;code&gt;常量区&lt;/code&gt;、&lt;code&gt;全局静态区&lt;/code&gt;、&lt;code&gt;栈区&lt;/code&gt;、&lt;code&gt;堆区&lt;/code&gt;。&lt;/p&gt;
&lt;h3 id="2-1. 内存的分区"&gt;2-1. 内存的分区&lt;/h3&gt;
&lt;p&gt;&lt;img src="/uploads/photo/james/36eea957-68ca-438d-9f10-11b22aa358b2.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h4 id="代码区"&gt;代码区&lt;/h4&gt;
&lt;p&gt;主要存放程序的可执行指令、函数符号表等信息，CPU 根据这些指令及符号表进行相应的操作。&lt;/p&gt;
&lt;h4 id="栈区"&gt;栈区&lt;/h4&gt;
&lt;p&gt;程序运行的实际“练兵场”，里面存放局部变量、形参、返回值等。&lt;/p&gt;
&lt;h4 id="堆区"&gt;堆区&lt;/h4&gt;
&lt;p&gt;是供程序在运行过程按需要使用的区域，在需要时申请，不需要时释放。申请堆内存，是指 CPU 根据代码区中的分配指令（&lt;code&gt;malloc&lt;/code&gt;对应的机器指令)，在运行的时候进行的；释放堆内存，是根据释放指令 (&lt;code&gt;free&lt;/code&gt;对应的机器指令)，也是在运行的时候进行的。&lt;/p&gt;
&lt;h4 id="其他的区"&gt;其他的区&lt;/h4&gt;
&lt;p&gt;这些区的叫法可能不太一样，主要用来存放&lt;code&gt;只读&lt;/code&gt;的数据，比如常量字符串、全局或静态变量等内容。这些内容，通常是在编译的时候就写死在可执行程序文件里面的。&lt;/p&gt;

&lt;p&gt;程序加载到内存时，操作系统会为进程分配一个栈空间和一个堆空间。栈主要是用来运行函数，堆用来保存运行时按需求分配的内容。栈与堆的大小都是有限的，这也就是调用一个没有返回条件的递归函数时，会导致耗尽栈空间，而被操作系统终止的原因。&lt;/p&gt;

&lt;p&gt;这两个空间，一开始可以认为是空白无瑕的，里面几乎没有数据。CPU 从 &lt;code&gt;代码区&lt;/code&gt; 中读取指令，若是遇到与内存操作相关的指令，要么对栈、要么对堆，做各种读写操作，比如分配内存或修其中的内容，经过这种一系列的操作后，这两个空间中的内容就会各有千秋。&lt;/p&gt;
&lt;h3 id="2-2. 栈与函数调用"&gt;2-2. 栈与函数调用&lt;/h3&gt;
&lt;p&gt;可以把这个第一次分配的栈看作是整个程序的最外层栈，&lt;code&gt;main&lt;/code&gt;函数就在这个栈空间中运行的，函数中声明的基本类型的变量，会存放在这个栈空间里。每一个函数被运行时，都会分配一个专门的函数栈，为了管理这些栈，有两个寄存器（栈底寄存器、栈顶寄存器）分别指向栈的底部和顶部。&lt;/p&gt;

&lt;p&gt;比如：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;i32&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;88&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;i32&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;CPU 会在栈空间中分配一个 4 个字节 (&lt;code&gt;i32&lt;/code&gt;) 空间，将其赋值为&lt;code&gt;88&lt;/code&gt;，然后再分配 4 个字节 (&lt;code&gt;i32&lt;/code&gt;) 的空间，将其赋值为&lt;code&gt;12&lt;/code&gt;。
每进行一次栈空间的分配，都会同时修改栈顶寄存器（&lt;code&gt;SP&lt;/code&gt;）的内容，让栈顶指针始终指向栈顶，上面代码执行完后时，&lt;code&gt;SP&lt;/code&gt;指向&lt;code&gt;b&lt;/code&gt;所代表的内容。&lt;/p&gt;

&lt;p&gt;当我们在一个函数内部，调用另一个函数时，会开辟一个新的栈空间以供这个被调用程序使用，这就是所谓的函数调用栈。&lt;/p&gt;

&lt;p&gt;比如：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;i32&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;i32&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;88&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;i32&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"a + b = {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;当调用 &lt;code&gt;add&lt;/code&gt;函数时，首先会保存调用方的（比如&lt;code&gt;main&lt;/code&gt;函数对应的栈等）现场信息，然后在当前栈顶 push 一个新的栈。在新的栈空间里，会分配相应的空间用于放置参数对应的内容，接着调用加法指令完成加法操作，接着把结果返回到调用方。返回的过程，其实就是把当前函数 (&lt;code&gt;add&lt;/code&gt;函数) 栈 pop 出去，再恢复现场信息，比如让栈底/栈顶寄存器分别指向原调用函数 (&lt;code&gt;main&lt;/code&gt;) 的函数栈。&lt;/p&gt;

&lt;p&gt;可见，对于栈空间的分配与释放过程，是由栈底与栈顶寄存器中的内容决定的，它是一个自动的过程。程序是无法显式更改其流程的。栈空间的分配与释放无非是两个寄存器内容的改变而已。所以，&lt;strong&gt;我们不用关心栈空间的分配与释放问题！&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="2-3. 指针类型"&gt;2-3. 指针类型&lt;/h3&gt;
&lt;p&gt;指针类型的变量，跟其他变量一样，本身都是内存中的一个位置，CPU 也是通过变量的名称找到这个位置。但是，这个位置对应的空间上存放的内容是描述另一个内存空间的位置信息，即内存的地址。&lt;/p&gt;

&lt;p&gt;之所以要专门介绍指针类型，是因为它是程序与堆内存交互的媒介，&lt;strong&gt;程序总是通过先访问保存在栈上指针，然后根据指针中存放的地址再去访问堆上空间。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;当然，指针类型的变量本身，既可以放在栈上，也可以放在堆上；指针类型变量中存放的值，既可以是栈上的内存地址，也可以是堆上的内存地址。只是大多数情况，指针变量本身存放在栈上，其存放的内容是堆上的空间地址。&lt;/p&gt;
&lt;h3 id="2-4. 堆空间"&gt;2-4. 堆空间&lt;/h3&gt;
&lt;p&gt;堆空间是程序在运行的时候按需使用到的那一部分内存，相当于“炒菜”中的那个大房间。当 CPU 读取到向堆分配内存的指令时，就会动态地分配一块堆内存空间。为了使用这一块分配的堆内存，需要记住这块堆内存的起始地址，所以，在分配完堆内存的同时，会返回它的起始地址，这个起始地址会用一个变量保存起来以备后用。这个变量就是一个经典的指针，通常是保存在栈空间中的。&lt;/p&gt;

&lt;p&gt;可见，&lt;strong&gt;要访问堆上的内容，要通过栈上指针类型的变量进行中转。CPU 对内存的视角，从这个意义上讲，只限制在栈内，栈才是程序运行的主戏台，堆顶多只是一个幕后者。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;与栈不同的是，堆空间的管理是离散的，分配堆内存前，无法预知其具体的地址。由于缺少栈空间管理自动性，所以必须的记住分配的堆内存地址，用以在不需要使用时，显式地释放堆内存。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;如果对堆上分配的内存空间管理不善，就会出现如下 3 个主要问题：&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;忘记释放不再需要使用的堆空间，导致内存泄露问题；&lt;/li&gt;
&lt;li&gt;当多个指针变量指向同一个堆地址空间时，通过其中一个指针变量释放后，再通过其他指针变量使用，会导致悬垂指针（野指针）问题；&lt;/li&gt;
&lt;li&gt;当多个指针变量指向同一个堆地址空间时，通过多个指针变量重复释放问题。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="2-5. 要解决的问题"&gt;2-5. 要解决的问题&lt;/h3&gt;
&lt;p&gt;内存管理的核心是对堆内存的管理，其要解决的核心问题就是，堆内存可能出现的上述 3 个问题，即&lt;code&gt;内存泄露&lt;/code&gt;、&lt;code&gt;悬垂指针&lt;/code&gt;、&lt;code&gt;重复释放&lt;/code&gt;。寻求对这三个问题的解决方案，是作为一门编程语言的第一要务。业界已有两种主流的解决方案，一种是&lt;code&gt;手动管理方式&lt;/code&gt;(比如&lt;code&gt;C/C++&lt;/code&gt;)，另一种是&lt;code&gt;垃圾回收管理方式&lt;/code&gt;(比如&lt;code&gt;Golang/Java&lt;/code&gt;)。不过，现在出现了第三种方案，那就是 Rust 提出的&lt;code&gt;所有权机制&lt;/code&gt; !&lt;/p&gt;
&lt;h2 id="3. 总结"&gt;3. 总结&lt;/h2&gt;
&lt;p&gt;本文就一个程序从源代码到运行的整个过程进行了系统地介绍，重点讨论了内存中栈与堆在程序运行中各自充当的角色以及两者的区别。栈才是程序运行的主戏台，要访问堆上的内容，要通过栈上指针类型的变量进行中转。栈内存的管理是自动的，堆是内存管理的重点与难点。要做好内存管理，其实质就是要解决可能出现的&lt;code&gt;内存泄露&lt;/code&gt;、&lt;code&gt;悬垂指针&lt;/code&gt;、&lt;code&gt;重复释放&lt;/code&gt;等问题。继&lt;code&gt;手动管理&lt;/code&gt;、&lt;code&gt;垃圾回收&lt;/code&gt;的内存管理方式后，Rust 中创造性地提出了&lt;code&gt;所有权&lt;/code&gt;机制。&lt;/p&gt;</description>
      <author>james</author>
      <pubDate>Thu, 30 May 2024 13:30:15 +0800</pubDate>
      <link>https://soldev.cn/topics/16</link>
      <guid>https://soldev.cn/topics/16</guid>
    </item>
    <item>
      <title>九层之台，起于累土 —— Rust 语言基础</title>
      <description>&lt;blockquote&gt;
&lt;p&gt;合抱之木，生于毫末；九层之台，起于累土；千里之行，始于足下。&lt;/p&gt;

&lt;p&gt;——《道德经》第六十四章&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;本文介绍 Rust 语言的基础内容，也是任何一门计算机语言中的通用的部分。其旨在让你快速上手 Rust 的编程，主要包括的内容有：&lt;code&gt;变量&lt;/code&gt;、&lt;code&gt;数据类型&lt;/code&gt;、&lt;code&gt;函数&lt;/code&gt;、&lt;code&gt;程序控制流&lt;/code&gt;等。学完这些内容后，应能掌握&lt;code&gt;面向过程式（OOP）&lt;/code&gt;编程，能较熟练地编写出用于解决诸如&lt;code&gt;查找与排序&lt;/code&gt;等数据结构与算法相关的问题。&lt;/p&gt;
&lt;h2 id="1. 变量与常量"&gt;1. 变量与常量&lt;/h2&gt;&lt;h3 id="1-1. 变量"&gt;1-1. 变量&lt;/h3&gt;
&lt;p&gt;几乎在我们所接触的所有语言中都存在&lt;code&gt;变量&lt;/code&gt;这个概念，在 Rust 中的变量，基本上等同于其他语言中的定义。&lt;code&gt;变量&lt;/code&gt;可以理解为一个名字，这个名字代表着内存中的某一块内存。内存中的内容当然会变化，这也是&lt;code&gt;变&lt;/code&gt;字所表达的最明显含义。当然，对于用于这个&lt;code&gt;名字&lt;/code&gt;的标识符范围也是有规则约束的，其规则遵同其他语言的通用规则，比如：&lt;code&gt;不能用数字开头&lt;/code&gt;、&lt;code&gt;不能使用诸如if之类的语言保留字&lt;/code&gt;等等。&lt;/p&gt;

&lt;p&gt;闲话少说，让我们直奔主题吧！&lt;/p&gt;
&lt;h4 id="定义变量"&gt;定义变量&lt;/h4&gt;&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;some_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;data_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;some_value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上面示例代码，是一个完整的变量定义语句。它表示定义了一个名为&lt;code&gt;some_type&lt;/code&gt;的变量，它的类型为&lt;code&gt;data_type&lt;/code&gt;，其对应的值为&lt;code&gt;some_value&lt;/code&gt;。
Rust 的编译器很智能，大部分情况下，可以省略&lt;code&gt;data_type&lt;/code&gt;，其类型可以由右边的值自动推断出正确的类型。&lt;/p&gt;

&lt;p&gt;这就完了，发现与其他语言没有什么区别。但 Rust 还有点不一样，接下来我们来说说它的不同之处吧。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;变量定义在 Rust 中有一个专门的叫法：&lt;code&gt;变量绑定&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;所有的变量默认是不可变的（这也可能是&lt;code&gt;绑定&lt;/code&gt;的原因吧）。&lt;/li&gt;
&lt;li&gt;要想让变量绑定的值能改变，在定义的时候，必须在变量名的前面显式地标注 &lt;code&gt;mut&lt;/code&gt;关键字。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;看一段代码：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"The value of x is: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"The value of x is: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;运行上面的程序，会得到类似如下的编译错误：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;2 |     &lt;span class="nb"&gt;let &lt;/span&gt;x &lt;span class="o"&gt;=&lt;/span&gt; 5&lt;span class="p"&gt;;&lt;/span&gt;
  |         -
  |         |
  |         first assignment to &lt;span class="sb"&gt;`&lt;/span&gt;x&lt;span class="sb"&gt;`&lt;/span&gt;
  |         &lt;span class="nb"&gt;help&lt;/span&gt;: consider making this binding mutable: &lt;span class="sb"&gt;`&lt;/span&gt;mut x&lt;span class="sb"&gt;`&lt;/span&gt;
3 |     println!&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"The value of x is: {}"&lt;/span&gt;, x&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
4 |     x &lt;span class="o"&gt;=&lt;/span&gt; 6&lt;span class="p"&gt;;&lt;/span&gt;
  |     ^^^^^ cannot assign twice to immutable variable

For more information about this error, try &lt;span class="sb"&gt;`&lt;/span&gt;rustc &lt;span class="nt"&gt;--explain&lt;/span&gt; E0384&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
error: could not compile &lt;span class="sb"&gt;`&lt;/span&gt;variables&lt;span class="sb"&gt;`&lt;/span&gt; due to previous error
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上面的错误指出错误的原因是 cannot assign twice to immutable variable &lt;code&gt;x&lt;/code&gt;（不能对不可变变量二次赋值），因为我们尝试给不可变的 x 变量赋值为第二个值。&lt;/p&gt;

&lt;p&gt;我们只需在上面的变量&lt;code&gt;x&lt;/code&gt;前面加上&lt;code&gt;mut&lt;/code&gt;关键字，程序就可以正常通过编译并输出预期的结果。&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="变量的遮蔽"&gt;变量的遮蔽&lt;/h4&gt;
&lt;p&gt;意思就是，第一个变量可以被第二个同名的变量所遮蔽（shadow），这意味着当我们使用变量时我们看到的会是第二个变量的值。变量遮蔽后，原先的那个被遮蔽的变量失效（对于后续的代码不可见）。我们可以通过使用相同的变量名并重复使用 let 关键字来遮蔽变量，如下所示：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"The value of x in the inner scope is: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"The value of x is: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;需要注意的是，&lt;code&gt;新变量&lt;/code&gt;与&lt;code&gt;被遮蔽的变量&lt;/code&gt;之间的关系，仅仅是名字在字面上一样，仅此而已！这就意味着，新变量可以有完全不同的类型，比如：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;//x 的类型由编译器自动推导为 `i32`&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"new value"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// x 的类型由编译器自动推导为 `&amp;amp;str`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="1-2. 常量"&gt;1-2. 常量&lt;/h3&gt;
&lt;p&gt;常量（constant）是绑定到一个常量名且不允许更改的值。但在 Rust 中，变量默认也是不可变的，那为什么还需要引入&lt;code&gt;常量&lt;/code&gt;的概念呢？两者间的差别有：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;常量不允许使用 mut。&lt;/li&gt;
&lt;li&gt;常量在定义（绑定）的时候，必须要显式初始化值，没有默认值。&lt;/li&gt;
&lt;li&gt;常量在定义（绑定）的时候，必须要显式地指定数据类型，编译器不会进行类型推导。&lt;/li&gt;
&lt;li&gt;常量可以在任意作用域内声明，包括全局作用域。&lt;/li&gt;
&lt;li&gt;常量只能设置为常量表达式，而不能是函数调用的结果或是只能在运行时计算得到的值。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;下面是一个定义常量的示例代码：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;DAY_IN_WEEK&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u8&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Rust 常量的命名约定是全部字母都使用大写，并使用下划线分隔单词。&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="2. 数据类型的本质"&gt;2. 数据类型的本质&lt;/h2&gt;
&lt;p&gt;运行程序所需要的数据，一般存储在内存中。内存元器件中的&lt;code&gt;高电平&lt;/code&gt;与&lt;code&gt;低电平&lt;/code&gt;的物理状态，被解读为&lt;code&gt;0&lt;/code&gt;和&lt;code&gt;1&lt;/code&gt;的&lt;code&gt;位（bit）&lt;/code&gt;，8 个&lt;code&gt;位（bit）&lt;/code&gt;为一个&lt;code&gt;字节（byte）&lt;/code&gt;。这里的&lt;code&gt;位&lt;/code&gt;、&lt;code&gt;字节&lt;/code&gt;都是一种数据类型。但仅仅用&lt;code&gt;位&lt;/code&gt;与&lt;code&gt;字节&lt;/code&gt;这种原始的类型，不足以（不便于）刻画我们的业务，于是就出现了诸如表示数字的&lt;code&gt;整型&lt;/code&gt;、表示一连串字符的&lt;code&gt;字符串&lt;/code&gt; 等等用于特定场景的有点业务味道的类型。不过，这种类型，通常是在机器架构层面提供的。作为一种语言的编译器层面，也当然会有对应之物。当然，进一步，语言层面，往往会提供更多的类型，以满足使该语言对业务建模的需求，比如，&lt;code&gt;结构体&lt;/code&gt;、&lt;code&gt;枚举&lt;/code&gt;、&lt;code&gt;类&lt;/code&gt;等等。&lt;/p&gt;

&lt;p&gt;每一个数据类型，都规定了该类型所占用的长度，比如在 32 位系统中，整型通常用占用 4 个字节（32 位），布尔类型占用 1 个字节（8 位）。即使整数&lt;code&gt;1&lt;/code&gt;只需要一个位来表示，也要占用 32 位。&lt;/p&gt;

&lt;p&gt;数据类型是对数据的抽象，不同的类型，其抽象的粒度不一样。数据类型，为 CPU 解读内存数据提供了共识。当 CPU 通过变量（内存地址的别名）去访问内存时，可以根据类型信息，知道把从内存位置开始到什么地方结束的这一块内存内容当成一个整体去解读。这个，对指针类型尤为明显，比如指针类型的&lt;code&gt;解指针&lt;/code&gt;和&lt;code&gt;作自增&lt;/code&gt;操作。&lt;/p&gt;

&lt;p&gt;内存中原本只是&lt;code&gt;0101...&lt;/code&gt;这种毫无意义的物件，这是一个混沌的世界。"神说要有光，于是就有了光"！这个神，肯定是我们道家中的神仙，他“道法自然”，点化万物，&lt;code&gt;0101...&lt;/code&gt;立马变成了各种生机勃勃的精灵，有红楼里轮回三生三世的绛珠仙草，有九龙潭的鳌、羚、鹰、鱼、虾、蛇，有沧海畔那“蝉眼龟形脚似蛛，未曾正面向人趋”的螃蠏侠，有... 于是就有了五色缤纷的世界，当然也有了险恶的江湖。对不起，扯远了。&lt;/p&gt;
&lt;h2 id="3. 基本类型"&gt;3. 基本类型&lt;/h2&gt;&lt;h3 id="3-1. 整数类型"&gt;3-1. 整数类型&lt;/h3&gt;
&lt;p&gt;整数是没有小数部分的数字， &lt;code&gt;i32&lt;/code&gt; 类型，表示有符号的 32 位整数（i 是英文单词 integer 的首字母，与之相反的是 u，代表无符号 unsigned 类型）。下表显示了 Rust 中的内置的整数类型：&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/james/51e82fc7-6d52-4188-ba7f-daa6c6587c14.jpg!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;类型定义的形式统一为：有无符号 + 类型大小 (位数)。无符号数表示数字只能取&lt;code&gt;正数&lt;/code&gt;和&lt;code&gt;0&lt;/code&gt;，而有符号则表示数字可以取&lt;code&gt;正数&lt;/code&gt;、&lt;code&gt;负数&lt;/code&gt;还有&lt;code&gt;0&lt;/code&gt;。就像在纸上写数字一样：当要强调符号时，数字前面可以带上正号或负号；然而，当很明显确定数字为正数时，就不需要加上正号了。有符号数字以补码形式存储。&lt;/p&gt;

&lt;p&gt;每个有符号类型规定的数字范围是 &lt;code&gt;-(2^n - 1) ~ 2^n - 1 - 1&lt;/code&gt;，其中 &lt;code&gt;n&lt;/code&gt; 是该定义形式的位长度。因此 &lt;code&gt;i8&lt;/code&gt; 可存储数字范围是 &lt;code&gt;-(2^7) ~ 2^7 - 1&lt;/code&gt;，即 &lt;code&gt;-128 ~ 127&lt;/code&gt;。无符号类型可以存储的数字范围是 &lt;code&gt;0 ~ 2^n - 1&lt;/code&gt;，所以 &lt;code&gt;u8&lt;/code&gt; 能够存储的数字为 &lt;code&gt;0 ~ 2^8 - 1&lt;/code&gt; ，即 &lt;code&gt;0 ~ 255&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;此外，&lt;code&gt;isize&lt;/code&gt; 和 &lt;code&gt;usize&lt;/code&gt; 类型取决于程序运行的计算机 CPU 类型：若 CPU 是 32 位的，则这两个类型是 32 位的，同理，若 CPU 是 64 位，那么它们则是 64 位。&lt;/p&gt;

&lt;p&gt;整形字面量可以用下表的形式书写：&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/james/8668d821-50f0-4036-b05c-d9fe9a3583d3.jpg!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;这么多类型，有没有一个简单的使用准则？答案是肯定的，Rust 整型默认使用 i32，例如 &lt;code&gt;let i = 1&lt;/code&gt;，那 i 就是 i32 类型，因此你可以首选它，同时该类型也往往是性能最好的。&lt;code&gt;isize&lt;/code&gt; 和 &lt;code&gt;usize&lt;/code&gt; 的主要应用场景是用作集合的索引。&lt;/p&gt;
&lt;h3 id="3-2. 浮点数类型"&gt;3-2. 浮点数类型&lt;/h3&gt;
&lt;p&gt;浮点数（floating-point number）是带有小数点的数字，在 Rust 中浮点类型（简称浮点型）数字也有两种基本类型。Rust 的浮点型是 f32 和 f64，它们的大小分别为 32 位和 64 位。默认浮点类型是 f64，因为在现代的 CPU 中它的速度与 f32 的几乎相同，但精度更高。所有浮点型都是有符号的。
下面是一个演示浮点数的示例：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 默认类型为： f64&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;f32&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;3.0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// f32&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;浮点数按照 IEEE-754 标准表示。f32 类型是单精度浮点型，f64 为双精度浮点型。&lt;/p&gt;

&lt;p&gt;Rust 的所有数字类型都支持基本数学运算：加法、减法、乘法、除法和取模运算。整数除法会向下取整。下面代码演示了各使用一条 let 语句来说明相应数字运算的用法：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// addition&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// subtraction&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;difference&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;95.5&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mf"&gt;4.3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// multiplication&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// division&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;quotient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;56.7&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;32.2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;floored&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Results in 0&lt;/span&gt;

    &lt;span class="c1"&gt;// remainder&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;remainder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;43&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="3-3. 字符类型"&gt;3-3. 字符类型&lt;/h3&gt;
&lt;p&gt;直接看代码：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'z'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'ℤ'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;heart_eyed_cat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'😻'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;有几点需要注意：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;声明的 &lt;code&gt;char&lt;/code&gt; 字面量采用单引号括起来，这与字符串字面量不同，字符串字面量是用双引号括起来。&lt;/li&gt;
&lt;li&gt;Rust 的字符类型大小为 4 个字节，表示的是一个 &lt;code&gt;Unicode&lt;/code&gt; 标量值，这意味着它可以表示的远远不止是 &lt;code&gt;ASCII&lt;/code&gt;。标音字母，中文/日文/韩文的文字，emoji，还有零宽空格 (zero width space) 在 Rust 中都是合法的字符类型。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="3-4. 布尔类型"&gt;3-4. 布尔类型&lt;/h3&gt;
&lt;p&gt;Rust 中的布尔类型有两个可能的值：true 和 false，布尔值占用内存的大小为 1 个字节：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 使用类型标注,显式指定f的类型&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"这是段毫无意义的代码"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用布尔类型的场景主要在于流程控制，例如上述代码的中的 if 就是其中之一。&lt;/p&gt;
&lt;h2 id="4. 复合类型"&gt;4. 复合类型&lt;/h2&gt;
&lt;p&gt;复合类型（compound type）可以将多个值组合成一个类型。Rust 有两种基本的复合类型：&lt;code&gt;元组（tuple）&lt;/code&gt; 和 &lt;code&gt;数组（array）&lt;/code&gt;。&lt;/p&gt;
&lt;h3 id="4-1. 元组"&gt;4-1. 元组&lt;/h3&gt;
&lt;p&gt;元组是将多种类型的多个值组合到一个复合类型中的一种基本方式。元组的长度是固定的：声明后，它们就无法增长或缩小。&lt;/p&gt;

&lt;p&gt;我们通过在小括号内写入以逗号分隔的值列表来创建一个元组。元组中的每个位置都有一个类型，并且元组中不同值的类型不要求是相同的。&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;tup&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;f64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;6.4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 定义元组的示例代码&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以通过下标索引访问：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"1st number is {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tup&lt;/span&gt;&lt;span class="na"&gt;.0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 0表示第一个元素，1表示第二个元素...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以解构：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;tup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;6.4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tup&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"The value of y is: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;没有任何值的元组是一种特殊的类型，只有一个值，也写成 &lt;code&gt;()&lt;/code&gt;。该类型被称为单元类型（unit type），该值被称为单元值（unit value）。如果表达式不返回任何其他值，就隐式地返回单元值。&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 单元类型 （）&lt;/span&gt;
&lt;span class="c1"&gt;// 单元类型在Rust中是非常重要的类型，如果表达式不返回任何其他值，就隐式地返回单元值，&lt;/span&gt;
&lt;span class="c1"&gt;// 如没有返回值的函数或者作用域&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;return_tuple&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;return_tuple&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nd"&gt;assert_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="4.2 数组"&gt;4.2 数组&lt;/h3&gt;
&lt;p&gt;数组的具体定义很简单：将多个类型相同的元素依次组合在一起，就是一个数组。数组的三要素：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;长度固定，一经定义就不可以改变&lt;/li&gt;
&lt;li&gt;元素必须有相同的类型&lt;/li&gt;
&lt;li&gt;依次线性排列&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;一个简单的示例：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 通过索引来访问或者修改数组中的元素&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt; &lt;span class="c1"&gt;// 编译器自动推导arr的类型为[i32;5] 数组&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;arr1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="n"&gt;arr1&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arr1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// [100, 0, 0, 0, 0]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;数组的正式类型： [元素的类型; 数组元素的总长度]，比如：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以使用下面的语法初始化一个某个值重复出现 N 次的数组：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt; &lt;span class="c1"&gt;// a 数组包含 5 个元素，这些元素的初始化值为 3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;访问数组时，索引的范围：[0, 数组长度 N)，即从 0 开始到长度 N-1。如果越界访问，会导致 Rust 运行时错误。&lt;/p&gt;

&lt;p&gt;至此，数组就介绍完了，最后有两个注意的点：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;数组是存储在栈上&lt;/li&gt;
&lt;li&gt;数组的长度也是类型的一部分，[u8; 3] 和 [u8; 4] 是不同的类型。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="5. 函数"&gt;5. 函数&lt;/h2&gt;
&lt;p&gt;函数其实也是一种数据类型，函数名称跟其他的变量名一样会关联到内存中代码区的某一个地方，代码将从这个地方开始执行代码。函数除了有名称，还有对应的具体类型，即函数签名，用来描述&lt;code&gt;调用函数需要需要传入的数据，即参数&lt;/code&gt; 及 &lt;code&gt;函数运行结束后返回的数据类型&lt;/code&gt; 等信息。&lt;/p&gt;

&lt;p&gt;Rust 函数签名如下：&lt;/p&gt;

&lt;p&gt;&lt;code&gt;fn func_name1(param1: type1, parm2: type2,...) -&amp;gt; return_type&lt;/code&gt; //完整函数的签名&lt;/p&gt;

&lt;p&gt;&lt;code&gt;fn func_name2() -&amp;gt; return_type&lt;/code&gt; // 无参数函数的签名&lt;/p&gt;

&lt;p&gt;&lt;code&gt;fn func_name3(param: type1)&lt;/code&gt; // 无返回值函数的签名，默认返回值的类型为 ()&lt;/p&gt;

&lt;p&gt;&lt;code&gt;fn func_name4()&lt;/code&gt; // 无参数且无返回值函数的签名&lt;/p&gt;

&lt;p&gt;在函数签名后面 加上一对大括号，写在这对括号内的代码叫做函数体。总体来说，Rust 函数与其他 C 系语言（比如 C/C++/Java 等）是类似的。上一段代码：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 1 函数定义&lt;/span&gt;
&lt;span class="c1"&gt;// 1.1 没有参数和返回值的函数&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"foo"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// 1.2 有参数和返回值的函数&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// 1.3 参数类型必须显式声明，比如引用或者可变性&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;foobar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"rust"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;s&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// 2 函数调用&lt;/span&gt;

&lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nf"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Rust"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;foobar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"go"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// 3 函数作为参数&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;a&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="n"&gt;value&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;b&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="mi"&gt;42&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// 把函数作为参传给另一个函数&lt;/span&gt;

&lt;span class="nf"&gt;a&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;有一个比较特殊的地方需要注意一下，就是函数返回值如果是&lt;code&gt;表达式&lt;/code&gt;，可以不用显式的加上&lt;code&gt;return&lt;/code&gt;关键字，代码末尾也不用加上&lt;code&gt;;&lt;/code&gt;号，比如上面的函数&lt;code&gt;b()&lt;/code&gt;中返库的&lt;code&gt;42&lt;/code&gt;。这种便捷技巧，请一定要掌握！在 Rust 中实践中应用得相当广泛。
Rust 把有&lt;code&gt;语句&lt;/code&gt;与&lt;code&gt;表达式&lt;/code&gt;之分，两者的区别，请记住一句话：“语句用来指示干什么工作，本身不返回值；否则就是表达式“。比如 &lt;code&gt;let apple = 5&lt;/code&gt; 就是指示做绑定操作，并没有返回值，所以它是语句；&lt;code&gt;42+1&lt;/code&gt;、&lt;code&gt;88&lt;/code&gt;，没有指示干什么操作，都是表达式。&lt;/p&gt;

&lt;p&gt;最后一点，就是如果函数没有显式返回，其返回的类型是&lt;code&gt;()&lt;/code&gt;，类似于 C 语言中的&lt;code&gt;void&lt;/code&gt;，表示什么都没有。&lt;/p&gt;
&lt;h2 id="6. 程序控制流"&gt;6. 程序控制流&lt;/h2&gt;&lt;h3 id="6-1. if 表达式"&gt;6-1. if 表达式&lt;/h3&gt;
&lt;p&gt;if 表达式允许根据条件执行不同的代码分支，跟其他语言没有什么区别。
看一段代码：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"number is divisible by 4"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"number is divisible by 3"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"number is divisible by 2"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"number is not divisible by 4, 3, or 2"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;条件必须为 bool 类型，这点与 C 语言是不一样的。&lt;/p&gt;
&lt;h4 id="在let语句中使用if"&gt;在 let 语句中使用 if&lt;/h4&gt;
&lt;p&gt;因为 if 是一个表达式，我们可以在 let 语句的右侧使用它来将结果赋值给一个变量，如下代码：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;condition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;condition&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"The value of number is: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;number 变量将会绑定到表示 if 表达式结果的值上。&lt;/p&gt;
&lt;h3 id="6-2. loop 循环"&gt;6-2. loop 循环&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;loop&lt;/code&gt;用来实现一个无限循环，可以使用 break 语句在任何时候退出一个循环，还可以使用 continue 跳过循环体的剩余部分并开始下一轮循环。如下面代码：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0u32&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Let's count until infinity!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// 无限循环&lt;/span&gt;
    &lt;span class="k"&gt;loop&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"three"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;// 跳过这次迭代的剩下内容&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"OK, that's enough"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;// 退出循环&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="嵌套循环和标签"&gt;嵌套循环和标签&lt;/h4&gt;
&lt;p&gt;在处理嵌套循环的时候可以 break 或 continue 外层循环。在这类情形中，循环必须用一些 'label（标签）来注明，并且标签必须传递给 break/continue 语句。&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#![allow(unreachable_code)]&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;'outer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;loop&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Entered the outer loop"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nv"&gt;'inner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;loop&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Entered the inner loop"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;// 这只是中断内部的循环&lt;/span&gt;
            &lt;span class="c1"&gt;//break;&lt;/span&gt;

            &lt;span class="c1"&gt;// 这会中断外层循环&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt; &lt;span class="nv"&gt;'outer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"This point will never be reached"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Exited the outer loop"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="从loop 循环中返回"&gt;从 loop 循环中返回&lt;/h4&gt;
&lt;p&gt;可以从&lt;code&gt;loop&lt;/code&gt;中返回值：将该值放在 break 之后，它就会被 loop 表达式返回。&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;loop&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;counter&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;counter&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt; &lt;span class="n"&gt;counter&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="nd"&gt;assert_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="6-3. while 循环"&gt;6-3. while 循环&lt;/h3&gt;
&lt;p&gt;while 关键字可以用作当型循环（当条件满足时循环）。&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 计数器变量&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"x is {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="6-4. for 循环"&gt;6-4. for 循环&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;for&lt;/code&gt; 循环是 Rust 的大杀器，相比其他的两种，在&lt;code&gt;性能&lt;/code&gt;与&lt;code&gt;安全&lt;/code&gt;方面都具有优势。&lt;code&gt;for&lt;/code&gt;事实上只是迭代器的一种语法糖，在这里不便于全面介绍清楚，在后面讨论迭代器的时修改再做详尽介绍。在此，仅跟出几种常用的范例。&lt;/p&gt;
&lt;h4 id="使用 for 遍历区间"&gt;使用 for 遍历区间&lt;/h4&gt;
&lt;p&gt;for in 结构可以遍历一个 Iterator（迭代器）。创建迭代器的一个最简单的方法是使用区间标记 &lt;code&gt;a..b&lt;/code&gt;。这会生成从 &lt;code&gt;a&lt;/code&gt;（包含此值）到 &lt;code&gt;b&lt;/code&gt;（不含此值）的，步长为 &lt;code&gt;1&lt;/code&gt; 的一系列值。&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;以上代码循环输出一个从 1 到 5 的序列。或者，可以使用 &lt;code&gt;a..=b&lt;/code&gt; 表示两端都包含在内的范围。上面的代码可以写成：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;..=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="使用for遍历集合"&gt;使用 for 遍历集合&lt;/h4&gt;
&lt;p&gt;用 for 遍历集合，其实质是遍历一个 Iterator（迭代器），运行时执行的是迭代器的相关方法。细节后面讨论迭代器时再说。&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;element&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"the value is: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="6.5 while vs for"&gt;6.5 while vs for&lt;/h3&gt;
&lt;p&gt;看两段完成同样功能代码：&lt;/p&gt;
&lt;h4 id="while实现的"&gt;while 实现的&lt;/h4&gt;&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"the value is: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

        &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;代码对数组中的元素进行计数。它从索引 0 开始，并接着循环直到遇到数组的最后一个索引（这时，&lt;code&gt;index &amp;lt; 5&lt;/code&gt; 不再为真）。这种通过索引访问元素的方式，由于越界会 panic，所以每一次循环都会检查&lt;code&gt;index&lt;/code&gt;的值，这就影响了性能。&lt;/p&gt;
&lt;h4 id="for 实现的"&gt;for 实现的&lt;/h4&gt;&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;element&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="nf"&gt;.iter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"the value is: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以看出，&lt;code&gt;for&lt;/code&gt; 并不会使用索引去访问数组，因此更安全也更简洁，同时避免 运行时的边界检查，性能更高。&lt;/p&gt;
&lt;h2 id="总结"&gt;总结&lt;/h2&gt;
&lt;p&gt;本文就语言的通用部分作了较系统的介绍，一些与 Rust 固有特色相关的概念未作深入探究。阅读完本文，读者应该掌握了 Rust 中的“十八般兵器”的基本使用。&lt;/p&gt;</description>
      <author>james</author>
      <pubDate>Sun, 26 May 2024 20:56:59 +0800</pubDate>
      <link>https://soldev.cn/topics/10</link>
      <guid>https://soldev.cn/topics/10</guid>
    </item>
    <item>
      <title> 与君初相识，犹如故人归 —— 初识 Rust</title>
      <description>&lt;blockquote&gt;
&lt;p&gt;与君初相识，犹如故人归。
天涯明月新，朝暮最相思。 
—— 杜牧《会友》&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;友情提示：本文项目中的内容直接来自于《The Rust Programming Language》中第 1 章与第 2 章（部分内容由 AI 翻译），可能冗长乏味，建议慎重阅读。若有未详尽或不准确之处，请直接阅读原书之内容。&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;在上一章里，介绍了 Rust 的一些基本情况，也搭建好了 Rust 的开发环境，但只是“空山不见人，但闻人语响”。现在，是时候约 Rust 出来见一面了！本文会手把手带你编写三个小项目，并对其中的代码进行细致地解读。有些内容不求你甚解，权当与 Rust 打个照面、混个面熟，也算是与 Rust 相识了。&lt;/p&gt;

&lt;p&gt;“与君初相识，犹如故人归”，自然甚好；要是“我断不思量，你莫思量我”，请不要原谅我！&lt;/p&gt;

&lt;p&gt;那就开始吧！&lt;/p&gt;
&lt;h2 id="Hello,World!"&gt;Hello,World!&lt;/h2&gt;
&lt;p&gt;作为程序界的惯例，我们选择从这个经典的程序开始，算是与 Rust 打一个招呼吧。&lt;/p&gt;
&lt;h3 id="1. 创建项目目录"&gt;1. 创建项目目录&lt;/h3&gt;
&lt;p&gt;对 Rust 来说，你的代码在哪里并不重要，但还是建议为 Rust 类型项目专门创建一个目录。我们建议在你的主目录中创建一个项目目录并将所有项目保存在那里。&lt;/p&gt;

&lt;p&gt;打开终端并输入以下命令以创建项目目录和“Hello, world!”目录 项目目录中的项目。&lt;/p&gt;

&lt;p&gt;对于  Linux、macOS 和 PowerShell，请输入：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; ~/projects
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/projects
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;hello_world
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;hello_world
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;对于 Windows CMD，输入：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="s2"&gt;"%USERPROFILE%&lt;/span&gt;&lt;span class="se"&gt;\p&lt;/span&gt;&lt;span class="s2"&gt;rojects"&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; /d &lt;span class="s2"&gt;"%USERPROFILE%&lt;/span&gt;&lt;span class="se"&gt;\p&lt;/span&gt;&lt;span class="s2"&gt;rojects"&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;mkdir &lt;/span&gt;hello_world
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;hello_world
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="2.编写和运行 Rust 程序"&gt;2.编写和运行 Rust 程序&lt;/h3&gt;
&lt;p&gt;接下来，创建一个新的源文件并将其命名为 main.rs。Rust 文件总是以.rs 扩展名结尾。如果您在文件名中使用多个单词，请使用下划线分隔它们。例如，使用 hello_world.rs 而不是 helloworld.rs。&lt;/p&gt;

&lt;p&gt;现在打开您刚刚创建的 main.rs 文件并输入如下代码：&lt;/p&gt;

&lt;p&gt;文件名：main.rs&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello, world!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;保存文件并返回终端窗口。在 Linux 或 macOS 上，输入以下命令来编译和运行文件：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;rustc main.rs
&lt;span class="nv"&gt;$ &lt;/span&gt;./main
Hello, world!
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在 Windows 上，输入命​​令.\main.exe 而不是./main：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; rustc main.rs
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; .&lt;span class="se"&gt;\m&lt;/span&gt;ain.exe
Hello, world!
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;无论您的操作系统是什么，字符串 Hello, world! 都应该打印到终端。如果您没有看到此输出，请参阅 安装部分的“疑难解答”部分以获得帮助。&lt;/p&gt;

&lt;p&gt;如果 Hello, world! 打印出来了，恭喜！你已经正式编写了一个 Rust 程序。这让你成为 Rust 程序员——欢迎！&lt;/p&gt;
&lt;h3 id="3. Rust 程序剖析"&gt;3. Rust 程序剖析&lt;/h3&gt;
&lt;p&gt;让我们详细回顾一下刚刚在您的“Hello, world!”中发生的事情。程序。这是拼图的第一部分：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这些行在 Rust 中定义了一个函数。这个 main 函数很特别：它总是在每个可执行的 Rust 程序中运行的第一个代码。第一行声明了一个名为的函数 main，它没有参数并且不返回任何内容。如果有参数，它们将放在&lt;code&gt;()&lt;/code&gt;内。&lt;/p&gt;

&lt;p&gt;另外，请注意，函数体用&lt;code&gt;{}&lt;/code&gt;括起来，将&lt;code&gt;左大括号&lt;/code&gt;与&lt;code&gt;函数声明&lt;/code&gt;放在同一行是一种很好的风格，在两者之间添加一个&lt;code&gt;空格&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;如果你想在 Rust 项目中坚持标准风格，你可以使用一个名为的自动格式化工具&lt;code&gt;rustfmt&lt;/code&gt;来格式化你的代码为特定风格。&lt;/p&gt;

&lt;p&gt;main 函数内部是以下代码：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello, world!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这一行完成了这个小程序中的所有工作：它将文本打印到屏幕上。这里有四个重要的细节需要注意。&lt;/p&gt;

&lt;p&gt;首先，Rust 风格是缩进四个空格，而不是制表符。&lt;/p&gt;

&lt;p&gt;其次，&lt;code&gt;println!&lt;/code&gt; 是 Rust &lt;code&gt;宏&lt;/code&gt;。如果它改为调用一个函数，它将被输入为&lt;code&gt;println&lt;/code&gt;（不带!）。&lt;/p&gt;

&lt;p&gt;第三，你看到了&lt;code&gt;"Hello, world!"&lt;/code&gt;字符串。我们将此字符串作为参数传递给&lt;code&gt;println!&lt;/code&gt;，然后将字符串打印到屏幕上。&lt;/p&gt;

&lt;p&gt;第四，我们用分号 ( ;) 结束这一行，这表示这个表达式已经结束，下一个表达式已准备好开始。大多数 Rust 代码行都以分号结尾。&lt;/p&gt;
&lt;h3 id="4. 编译和运行是分开的步骤"&gt;4. 编译和运行是分开的步骤&lt;/h3&gt;
&lt;p&gt;您刚刚运行了一个新创建的程序，因此让我们检查该过程中的每个步骤。&lt;/p&gt;

&lt;p&gt;在运行 Rust 程序之前，您必须使用 Rust 编译器通过输入&lt;code&gt;rustc&lt;/code&gt;命令并将源文件的名称传递给它来编译它，如下所示：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;rustc main.rs
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果您有 C 或 C++ 背景，您会注意到这类似于 gcc 或者 clang。编译成功后，Rust 会输出一个二进制可执行文件。&lt;/p&gt;

&lt;p&gt;在 Linux、macOS 和 Windows 上的 PowerShell 上，您可以通过 ls 在 shell 中输入命令来查看可执行文件。在 Linux 和 macOS 上，您将看到两个文件。使用 Windows 上的 PowerShell，您将看到与使用 CMD 相同的三个文件。&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;ls
&lt;/span&gt;main  main.rs
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在 Windows 上使用 CMD，您将输入以下内容：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dir&lt;/span&gt; /B %&lt;span class="o"&gt;=&lt;/span&gt; the /B option says to only show the file names &lt;span class="o"&gt;=&lt;/span&gt;%
main.exe
main.pdb
main.rs
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这显示了带有.rs 扩展名的源代码文件、可执行文件（在 Windows 上为 main.exe，但在所有其他平台上为 main），以及在使用 Windows 时，包含带有.pdb 扩展名的调试信息的文件。从这里，您运行 main 或 main.exe 文件，如下所示：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;./main &lt;span class="c"&gt;# or .\main.exe on Windows&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;执行上面的命令，此行将打印&lt;code&gt;Hello, world!&lt;/code&gt;到您的终端。&lt;/p&gt;

&lt;p&gt;如果您更熟悉动态语言，例如 Ruby、Python 或 JavaScript，您可能不习惯将程序作为单独的步骤进行编译和运行。Rust 是一种提前编译的语言，这意味着您可以编译程序并将可执行文件提供给其他人，即使没有安装 Rust，他们也可以运行它。如果您给某人一个.rb、.py 或 .js 文件，他们需要（分别）安装一个 Ruby、Python 或 JavaScript 实现。但是在那些语言中，您只需要一个命令来编译和运行您的程序。一切都是语言设计的权衡。&lt;/p&gt;

&lt;p&gt;对于简单的程序来说，只用&lt;code&gt;rustc&lt;/code&gt;编译就可以了，但是随着项目的增长，您将需要管理所有选项并轻松共享您的代码。接下来，我们将向您介绍 Cargo 工具，它将帮助您编写真实的 Rust 程序。&lt;/p&gt;
&lt;h2 id="Hello,Cargo!"&gt;Hello,Cargo!&lt;/h2&gt;
&lt;p&gt;Cargo 是 Rust 的构建系统和包管理器。大多数 Rustaceans 使用这个工具来管理他们的 Rust 项目，因为 Cargo 会为您处理很多任务，例如构建代码、下载代码所依赖的库以及构建这些库。&lt;/p&gt;

&lt;p&gt;最简单的 Rust 程序，就像我们目前所写的一样，没有任何依赖关系。因此，如果我们构建了“Hello, world!”与 Cargo 合作的项目，它只会使用 Cargo 中处理构建代码的部分。当您编写更复杂的 Rust 程序时，您将添加依赖项，如果您使用 Cargo 启动项目，添加依赖项将更容易做到。&lt;/p&gt;

&lt;p&gt;正常情况下，Cargo 会随 Rust 一起安装。如果您通过其他方式安装了 Rust，请通过在终端中输入以下内容来检查是否安装了 Cargo：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;cargo &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果你看到一个版本号，你有它！如果您看到错误，例如 command not found，请查看您的安装方法的文档以确定如何单独安装 Cargo。&lt;/p&gt;
&lt;h3 id="1. 使用Cargo创建项目"&gt;1. 使用 Cargo 创建项目&lt;/h3&gt;
&lt;p&gt;让我们使用 Cargo 创建一个新项目，看看它与我们原来的“Hello, world!”有何不同。导航回您的项目目录（或您决定存储代码的任何位置）。然后，在任何操作系统上，运行以下命令：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;cargo new hello_cargo
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;hello_cargo
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;第一个命令创建了一个名为&lt;code&gt;hello_cargo&lt;/code&gt;的新目录。由于我们将项目命名为&lt;code&gt;hello_cargo&lt;/code&gt;，Cargo 在同名目录中创建其文件。&lt;/p&gt;

&lt;p&gt;进入&lt;code&gt;hello_cargo&lt;/code&gt;目录并列出文件。您会看到 Cargo 为我们生成了两个文件和一个目录：一个&lt;code&gt;Cargo.toml文件&lt;/code&gt;和一个 &lt;code&gt;src目录&lt;/code&gt;，其中包含一个&lt;code&gt;main.rs&lt;/code&gt;文件。&lt;/p&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="err"&gt;$&lt;/span&gt; &lt;span class="err"&gt;tree&lt;/span&gt; &lt;span class="err"&gt;-L&lt;/span&gt; &lt;span class="err"&gt;2&lt;/span&gt;
&lt;span class="err"&gt;.&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="err"&gt;Cargo.toml&lt;/span&gt;
&lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="err"&gt;src&lt;/span&gt;
    &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="err"&gt;main.rs&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;其中&lt;code&gt;Cargo.toml&lt;/code&gt;的内容，类似如下清单：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;package]
name &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hello_cargo"&lt;/span&gt;
version &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"0.1.0"&lt;/span&gt;
edition &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"2021"&lt;/span&gt;

&lt;span class="o"&gt;[&lt;/span&gt;dependencies]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;第一行的&lt;code&gt;[package]&lt;/code&gt;，是一个节标题，指示以下语句正在配置包。随着我们向此文件添加更多信息，我们将添加其他部分。&lt;/p&gt;

&lt;p&gt;接下来的三行设置，是 Cargo 编译程序所需的配置信息：名称、版本和要使用的 Rust 版本。&lt;/p&gt;

&lt;p&gt;最后一行，&lt;code&gt;[dependencies]&lt;/code&gt;，是您列出项目的任何依赖项的部分的开始。在 Rust 中，代码包被称为 crates。这个项目我们不需要任何其他 crate，但我们将在下一个“猜数游戏”项目中使用。
现在打开 src/main.rs 看看：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello, world!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;记得把代码中的"Hello, world!"，改成"Hello, Cargo!" &lt;/p&gt;

&lt;p&gt;到目前为止，我们之前的项目与 Cargo 生成的项目的不同之处在于 Cargo 将代码放在了 src 目录中，且在项目根目录中有一个&lt;code&gt;Cargo.toml&lt;/code&gt;配置文件。&lt;/p&gt;

&lt;p&gt;Cargo 希望您的源文件位于 src 目录中。项目根目录仅用于存放 &lt;code&gt;README文件、许可证信息、配置文件&lt;/code&gt;以及与您的代码无关的任何其他内容。&lt;/p&gt;

&lt;p&gt;如果您启动了一个不使用 Cargo 的项目，就像我们使用“Hello, world!”所做的那样 项目，您可以将其转换为使用 Cargo 的项目。方法是，将项目代码移动到 src 目录并创建一个适当的 Cargo.toml 文件。&lt;/p&gt;
&lt;h3 id="2. 建立和运行项目"&gt;2. 建立和运行项目&lt;/h3&gt;
&lt;p&gt;在 hello_cargo 目录中，通过输入以下命令构建项目：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;cargo build
   Compiling hello_cargo v0.1.0 &lt;span class="o"&gt;(&lt;/span&gt;/Users/mjsong/Desktop/rust_study/hello_cargo&lt;span class="o"&gt;)&lt;/span&gt;
    Finished &lt;span class="sb"&gt;`&lt;/span&gt;dev&lt;span class="sb"&gt;`&lt;/span&gt; profile &lt;span class="o"&gt;[&lt;/span&gt;unoptimized + debuginfo] target&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;0.68s
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;此命令在&lt;code&gt;target/debug/hello_cargo&lt;/code&gt;（或 Windows 上的&lt;code&gt;target\debug\hello_cargo.exe&lt;/code&gt;）而不是在您的当前目录中创建一个可执行文件。&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; &lt;span class="nv"&gt;$ &lt;/span&gt;tree &lt;span class="nt"&gt;-L&lt;/span&gt; 2
&lt;span class="nb"&gt;.&lt;/span&gt;
├── Cargo.lock
├── Cargo.toml
├── src
│   └── main.rs
└── target
    ├── CACHEDIR.TAG
    └── debug
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;您可以使用以下命令运行可执行文件：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;./target/debug/hello_cargo &lt;span class="c"&gt;# or .\target\debug\hello_cargo.exe on Windows&lt;/span&gt;
Hello, Cargo!
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cargo 还会在项目根目录下创建一个&lt;code&gt;Cargo.lock&lt;/code&gt;文件，该文件跟踪项目中依赖项的确切版本。您永远不需要手动更改此文件，Cargo 为您自动管理其中的内容。&lt;/p&gt;

&lt;p&gt;我们刚刚构建了一个项目 cargo build 并使用 运行它 ./target/debug/hello_cargo，但我们也可以使用它 cargo run 来编译代码，然后在一个命令中运行生成的可执行文件：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="err"&gt;$&lt;/span&gt; &lt;span class="n"&gt;cargo&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt;
    &lt;span class="n"&gt;Finished&lt;/span&gt; &lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt; &lt;span class="n"&gt;profile&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;unoptimized&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;debuginfo&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="nf"&gt;target&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mf"&gt;0.01&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
     &lt;span class="n"&gt;Running&lt;/span&gt; &lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;hello_cargo&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;
&lt;span class="n"&gt;Hello&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;Cargo!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cargo 还提供了一个名为 cargo check. 此命令快速检查您的代码以确保它可以编译但不会生成可执行文件：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;cargo check
    Checking hello_cargo v0.1.0 &lt;span class="o"&gt;(&lt;/span&gt;/Users/mjsong/Desktop/rust_study/hello_cargo&lt;span class="o"&gt;)&lt;/span&gt;
    Finished &lt;span class="sb"&gt;`&lt;/span&gt;dev&lt;span class="sb"&gt;`&lt;/span&gt; profile &lt;span class="o"&gt;[&lt;/span&gt;unoptimized + debuginfo] target&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;0.09s
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;为什么你不想要一个可执行文件？通常，&lt;code&gt;cargo check&lt;/code&gt;比&lt;code&gt;cargo build&lt;/code&gt;快得多，因为它跳过了生成可执行文件的步骤。如果您在编写代码时不断检查您的工作，使用&lt;code&gt;cargo check&lt;/code&gt;将加快该过程！因此，定期&lt;code&gt;cargo check&lt;/code&gt;确保程序可以编译是一个推荐的做法。&lt;/p&gt;

&lt;p&gt;让我们回顾一下到目前为止我们对 Cargo 的了解：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;我们可以使用&lt;code&gt;cargo build&lt;/code&gt;，编译程序。&lt;/li&gt;
&lt;li&gt;我们可以使用&lt;code&gt;cargo run&lt;/code&gt;，编译并运行程序。&lt;/li&gt;
&lt;li&gt;我们可以使用&lt;code&gt;cargo check&lt;/code&gt;，在不生成二进制文件的情况下构建一个项目。&lt;/li&gt;
&lt;li&gt;Cargo 没有将构建结果保存在与我们的代码相同的目录中，而是将其存储在&lt;code&gt;target/debug&lt;/code&gt;目录中。&lt;/li&gt;
&lt;li&gt;使用 Cargo 的另一个优点是，无论您使用哪种操作系统，命令都是相同的。因此，我们将不再提供针对 Linux 和 macOS 与 Windows 的具体说明。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="3. 为发布而构建"&gt;3. 为发布而构建&lt;/h3&gt;
&lt;p&gt;当您的项目最终准备好发布时，您可以使用 &lt;code&gt;cargo build --release&lt;/code&gt; 优化来编译它。此命令将在&lt;code&gt;target/release&lt;/code&gt;而不是&lt;code&gt;target/debug&lt;/code&gt;中创建可执行文件。优化使您的 Rust 代码运行得更快，但打开它们会延长程序编译所需的时间。这就是为什么有两种不同的配置文件的原因：一个用于开发，当您想要快速且经常重建时，另一个用于构建您将提供给用户的最终程序。如果您要对代码的运行时间进行基准测试，请务必使用 &lt;code&gt;cargo build --release&lt;/code&gt; 构建项目。&lt;/p&gt;
&lt;h3 id="4. Cargo作为公约"&gt;4. Cargo 作为公约&lt;/h3&gt;
&lt;p&gt;对于简单的项目，Cargo 并没有提供比&lt;code&gt;rustc&lt;/code&gt;更多的价值，但是随着你的程序变得更加复杂，它会证明它的价值。对于由多个 crate 组成的复杂项目，让 Cargo 协调构建要容易得多。&lt;/p&gt;

&lt;p&gt;尽管这个 hello_cargo 项目很简单，但它现在使用了很多你将在 Rust 职业生涯中使用的真正工具。事实上，要处理任何现有项目，您可以使用以下命令使用 Git 签出代码，切换到该项目的目录并构建：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git clone example.org/someproject
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;someproject
&lt;span class="nv"&gt;$ &lt;/span&gt;cargo build
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="猜数游戏"&gt;猜数游戏&lt;/h2&gt;
&lt;p&gt;项目的需求：程序将生成一个介于 1 和 100 之间的随机整数。然后它会提示玩家输入猜测。输入猜测值后，程序将指示猜测值是过低还是过高。如果猜对了，游戏将打印祝贺信息并退出。&lt;/p&gt;
&lt;h3 id="1. 新建项目"&gt;1. 新建项目&lt;/h3&gt;
&lt;p&gt;我们同样采用 Cargo 创建与管理项目，执行如下命令：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;cargo new guessing_game
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;guessing_game
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;项目已经创建好了，其效果跟上一个项目一样，如果编译运行会在终端打印名称“Hello,world!"，我们将在基础上进行迭代，一步步地完成我们的需求。但作为一个习惯，在继续新代码之前，我们要用 &lt;code&gt;cargo check&lt;/code&gt; 验证一下之前的代码是否有问题。&lt;/p&gt;
&lt;h3 id="2. 处理猜测"&gt;2. 处理猜测&lt;/h3&gt;
&lt;p&gt;猜谜游戏程序的第一部分将要求用户输入，处理该输入，并检查输入是否符合预期。在&lt;code&gt;src/main.rs&lt;/code&gt;中输入如下代码。&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Guess the number!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Please input your guess."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;guess&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;stdin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="nf"&gt;.read_line&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;guess&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;.expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to read line"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"You guessed: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;guess&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这段代码包含很多信息，所以让我们逐行查看。要获取用户输入然后将结果打印为输出，我们需要将 &lt;code&gt;io（输入/输出库）&lt;/code&gt;从标准库（也就是 std）引入到项目中。&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;作为默认行为，Rust 会将预导入模块内的条目自动引入每一段程序的作用域中，它包含了一小部分相当常用的类型。但假如你需要的类型不在预导入模块内，那么我们就必须用 use 语句来显式地进行导入声明。std::io 库包含了许多有用的功能，我们可以使用它来获得用户的输入数据。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;main&lt;/code&gt;函数是程序的入口点：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;fn&lt;/code&gt; 表示声明一个新的函数，&lt;code&gt;main&lt;/code&gt;表示函数的名称（main 有特定的含义，表示整个程序的入口程序），&lt;code&gt;()&lt;/code&gt;里面可以有参数，&lt;code&gt;{&lt;/code&gt;表示函数体的开始，&lt;code&gt;}&lt;/code&gt;表示函数体的结束，在&lt;code&gt;{}&lt;/code&gt;可以写实现函数功能的具体代码。
&lt;code&gt;println!&lt;/code&gt; 宏可以将字符串打印到屏幕上：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Guess the number!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Please input your guess."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;此代码正在打印一个提示，说明游戏是什么并请求用户输入。&lt;/p&gt;
&lt;h3 id="3. 用变量存储值"&gt;3. 用变量存储值&lt;/h3&gt;
&lt;p&gt;接下来，我们将创建一个变量来存储用户输入，如下所示：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;guess&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Rust 中用&lt;code&gt;let&lt;/code&gt;来绑定一个变量（在其他语言叫定义或者声明），上面的语句的意思是指，我们把右边的值（字符串对象，现在可以理解为一个空的字符串）绑定到左边的名为&lt;code&gt;guess&lt;/code&gt;的变量上。
这里有一个值得注意的地方是在 &lt;code&gt;guess&lt;/code&gt;前面多了一个&lt;code&gt;mut&lt;/code&gt;，它的意思是，&lt;code&gt;guess&lt;/code&gt;变量的值在之后还会变化，这也是符合我们需求的，因为它将用来保存用户的输入。在 Rust 世界中，所有的变量在绑定（定义）之后，是不能改变的，除非绑定（定义）的时候在名称前面显式地标 &lt;code&gt;mut&lt;/code&gt; 。
看一下下面的代码：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;apples&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;//不能改变&lt;/span&gt;
&lt;span class="c1"&gt;//apples = 8; //此处会导致编译错误&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;oranges&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;oranges&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// ok，因为定义oranges时标注为 mut&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;现在顺便来说一下&lt;code&gt;String::new()&lt;/code&gt;的意思，new 是该 String 类型的关联函数，关联函数是在类型上实现的函数，在本例中为 String. 它会返回一个 String 实例给左边的变量&lt;code&gt;guess&lt;/code&gt;。String 是标准库提供的字符串类型，是可增长的 UTF-8 编码的文本位。关于 String 的情况，目前也只能就此打住，后面会专门地讲解它。&lt;/p&gt;
&lt;h3 id="4.接收用户输入"&gt;4.接收用户输入&lt;/h3&gt;&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;stdin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;.read_line&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;guess&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;程序开头通过&lt;code&gt;use std::io&lt;/code&gt;导入 io 库，&lt;code&gt;io::stdin()&lt;/code&gt;会获取到 std::io::Stdin 实例，表示终端标准输入句柄的类型。
调用 &lt;code&gt;read_line(&amp;amp;mut guess)&lt;/code&gt; ，可以获取用户的输入。我们还将&lt;code&gt;&amp;amp;mut guess&lt;/code&gt; 作为参数传递&lt;code&gt;read_line&lt;/code&gt; 给它以告诉它存储用户输入的字符串。&lt;/p&gt;

&lt;p&gt;上面这段代码的意思是，将用户输入的任何内容放入标准输入并将其附加到字符串中（不覆盖其内容），所以我们因此将该字符串作为参数传递。字符串参数需要是可变的，以便该方法可以更改字符串的内容。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;amp;&lt;/code&gt; 表示此参数是一个引用，它为您提供了一种方法，让您的代码的多个部分访问一个数据，而无需多次将该数据复制到内存中。引用是一个复杂的特性，Rust 的主要优势之一是使用引用是多么安全和容易。你不需要知道很多细节来完成这个程序。现在，您需要知道的是，与变量一样，引用在默认情况下是不可变的。因此，您需要编写 &lt;code&gt;&amp;amp;mut guess&lt;/code&gt;而不是 &lt;code&gt;&amp;amp;guess&lt;/code&gt; 使其可变。&lt;/p&gt;
&lt;h3 id="5. 使用Result类型处理潜在异常"&gt;5. 使用 Result 类型处理潜在异常&lt;/h3&gt;
&lt;p&gt;尽管我们现在讨论的是第三行文本，但它仍然是单个逻辑代码行的一部分。下一部分是这个方法：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nf"&gt;.expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to read line"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们可以把这段代码写成：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;stdin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.read_line&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;guess&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to read line"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但是，一长行很难阅读，因此最好将其分开。当您使用.method_name() 语法调用方法时，引入换行符和其他空格以帮助分解长行通常是明智的。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;read_line()&lt;/code&gt; 从标准输入中获取用户的输入并把输入的内容附加到字符串，同时会返回一个&lt;code&gt;io::Result&lt;/code&gt; 类型的实例。&lt;code&gt;Result&lt;/code&gt;是一个枚举类型，枚兴类型由一系列固定的值组合面成。这些值被称作枚举的变体。对于&lt;code&gt;Result&lt;/code&gt;而言，它拥有&lt;code&gt;Ok&lt;/code&gt; 和 &lt;code&gt;Err&lt;/code&gt; 两个变体。其中&lt;code&gt;Ok&lt;/code&gt;变体表明当前的操作执行成功，并附带执行后产生的结果。相应地，&lt;code&gt;Err&lt;/code&gt; 变体则表明当前的操作执行失败，并附带执行后引发失败的具体原因。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Result&lt;/code&gt; 类型的值定义了一系列的方法，&lt;code&gt;expect&lt;/code&gt; 就是其中之一。例如 &lt;code&gt;io::Result&lt;/code&gt; 实例的值是&lt;code&gt;Err&lt;/code&gt;，那么&lt;code&gt;expect&lt;/code&gt; 方法就会中断当前的程序，并将传入的字符串显示出来；如果是&lt;code&gt;Ok&lt;/code&gt;，就会取出&lt;code&gt;Ok&lt;/code&gt;中附带的值，并将它作为结果返回给用户。&lt;/p&gt;

&lt;p&gt;如果你不调用 &lt;code&gt;expect&lt;/code&gt;方法，编译程序的话，会收到类似警告：&lt;code&gt;note: this&lt;/code&gt;Result&lt;code&gt;may be an&lt;/code&gt;Err&lt;code&gt;variant, which should be handled&lt;/code&gt; 。&lt;/p&gt;
&lt;h3 id="6. println!使用占位符打印值"&gt;6. println! 使用占位符打印值&lt;/h3&gt;
&lt;p&gt;除了右大括号之外，到目前为止，代码中只有一行要讨论：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"You guessed: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;guess&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;此行打印现在包含用户输入的字符串。可以将{}想象成一个固定值的占位符，您可以使用花括号打印多个值：第一组花括号保存格式字符串后列出的第一个值，第二组保存第二个值，依此类推。在一次调用中打印多个值 println! 如下所示：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"x = {} and y = {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;此代码将打印 x = 5 and y = 10.&lt;/p&gt;
&lt;h3 id="7. 测试一下程序的效果"&gt;7. 测试一下程序的效果&lt;/h3&gt;
&lt;p&gt;现在，让我们来运行一下上面的代码：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cargo run
   Compiling guessing_game v0.1.0 &lt;span class="o"&gt;(&lt;/span&gt;/Users/mjsong/Desktop/rust_study/guessing_game&lt;span class="o"&gt;)&lt;/span&gt;
    Finished &lt;span class="sb"&gt;`&lt;/span&gt;dev&lt;span class="sb"&gt;`&lt;/span&gt; profile &lt;span class="o"&gt;[&lt;/span&gt;unoptimized + debuginfo] target&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;1.71s
     Running &lt;span class="sb"&gt;`&lt;/span&gt;target/debug/guessing_game&lt;span class="sb"&gt;`&lt;/span&gt;
Guess the number!
Please input your guess.
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;88
You guessed: 88
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;到目前为此，我们已经完成了猜数游戏的第一部分：可以从用户的键盘获得输入并将它们打印出来。&lt;/p&gt;
&lt;h3 id="8. 生成一个保密数字"&gt;8. 生成一个保密数字&lt;/h3&gt;
&lt;p&gt;接下来，我们需要生成一个保密的数字来供玩家进行猜测。需要保证每一次数字不一样，且在 1-100 之间。&lt;/p&gt;
&lt;h4 id="8-1. 使用 Cargo 获取依赖"&gt;8-1. 使用 Cargo 获取依赖&lt;/h4&gt;
&lt;p&gt;这显然是一个随机数问题，只是 Rust 标准库中没有既有的随机数库可用。Crates.io 是 Rust 生态系统中的人们发布他们的开源 Rust 项目供其他人使用的地方。所以，我们可以到&lt;code&gt;crates.io&lt;/code&gt;仓库中，去寻找有没有现成的轮子可用。&lt;/p&gt;

&lt;p&gt;首先，打开 cargo 仓库（ &lt;a href="https://crates.io/" rel="nofollow" target="_blank"&gt;https://crates.io/&lt;/a&gt; ），尝试查询 "rand" 关键字：
&lt;img src="/uploads/photo/james/42dcd789-7a81-4b08-9ab0-18b598a4538b.jpg!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;排在列首是&lt;code&gt;rand&lt;/code&gt; 包（crate），点进去查看文档发现其功能满足我们生成随机数的需求，当前最新版是&lt;code&gt;0.8.5&lt;/code&gt; 。
&lt;img src="/uploads/photo/james/d38950e6-6100-4970-a686-5109f81f1cd5.jpg!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;现在该 Cargo 发挥作用的时候了，打开&lt;code&gt;Cargo.toml&lt;/code&gt;文件，悠改如下类似代码，增加&lt;code&gt;rand&lt;/code&gt;包的依赖。&lt;/p&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[package]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"guessing_game"&lt;/span&gt;
&lt;span class="py"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.1.0"&lt;/span&gt;
&lt;span class="py"&gt;edition&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"2021"&lt;/span&gt;

&lt;span class="nn"&gt;[dependencies]&lt;/span&gt;
&lt;span class="py"&gt;rand&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.8.5"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但一种更好的办法是用&lt;code&gt;cargo add&lt;/code&gt; 命令增加依赖包，比如在项目目录下，执行：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;cargo add rand
    Updating &lt;span class="sb"&gt;`&lt;/span&gt;ustc&lt;span class="sb"&gt;`&lt;/span&gt; index
      Adding rand v0.8.5 to dependencies
             Features:
             + alloc
             + getrandom
             + libc
             + rand_chacha
             + std
             + std_rng
             - log
             - min_const_gen
             - nightly
             - packed_simd
             - serde
             - serde1
             - simd_support
             - small_rng
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样会自动更新 &lt;code&gt;Cargo.toml&lt;/code&gt; 文件中的内容，并成功把&lt;code&gt;依赖包及依赖包所依赖的包&lt;/code&gt;下载到本地，然后编译这些依赖。
如果是直接在&lt;code&gt;Cargo.toml&lt;/code&gt;的&lt;code&gt;[dependencies]&lt;/code&gt;文件中手动填写依赖，则需要在执行 &lt;code&gt;cargo build&lt;/code&gt; 或 &lt;code&gt;cargo check&lt;/code&gt; 时，才会下载、编译 &lt;code&gt;依赖包及其依赖包所依赖的包&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;在&lt;code&gt;Cargo.toml&lt;/code&gt;文件中，标题后面的所有内容都是该部分的一部分，该部分一直持续到另一个部分开始。在&lt;code&gt;[dependencies]&lt;/code&gt;您告诉 Cargo 您的项目依赖于哪些外部 crates 以及您需要这些 crates 的哪些版本。&lt;/p&gt;

&lt;p&gt;Cargo 非常的智能，&lt;code&gt;cargo build&lt;/code&gt; 后，如果我们没有对 &lt;code&gt;Cargo.toml&lt;/code&gt;文件作任何的修改，再次执行 &lt;code&gt;cargo build&lt;/code&gt;，会跳过下载、编译所依赖的第三方库。&lt;/p&gt;
&lt;h4 id="8-2. 通过Cargo.lock文件确保可重现的构建"&gt;8-2. 通过 Cargo.lock 文件确保可重现的构建&lt;/h4&gt;
&lt;p&gt;&lt;em&gt;注意：原书中的内容有误，以下内容作过修正。&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Cargo 有一种机制，可确保您每次或其他任何人构建代码时都可以重建相同的产物。在你第一次使用&lt;code&gt;cargo build&lt;/code&gt;后，会在项目目录中生成该文件，根据依赖的&lt;strong&gt;语义化版本&lt;/strong&gt; 找到符合要求的具体版本号，并将其写入到该文件中。&lt;/p&gt;

&lt;p&gt;在这里，我们指定版本为 &lt;code&gt;0.8.5&lt;/code&gt; , Cargo 会按照标准的&lt;strong&gt;语义化版本&lt;/strong&gt;系统（有时称为 SemVer）解读具体的含义。该数字 &lt;code&gt;0.8.5&lt;/code&gt; 实际上是 &lt;code&gt;^0.8.5&lt;/code&gt; 一种简写，这意味着至少&lt;code&gt;0.8.5&lt;/code&gt;，但低于的任何版本&lt;code&gt;0.9.0&lt;/code&gt; ，但是不保证任何版本 &lt;code&gt;0.9.0&lt;/code&gt; 或 更高版本 都具有与以下示例使用的相同的 API。&lt;/p&gt;

&lt;p&gt;如果当前&lt;code&gt;0.8.5&lt;/code&gt;是最新版，会在&lt;code&gt;.lock&lt;/code&gt;文件中写入&lt;code&gt;0.8.5&lt;/code&gt;，并用该版本构建；如果之后出了一个 &lt;code&gt;0.8.6&lt;/code&gt;，再次构建时，会将文件中的版本修改为&lt;code&gt;0.8.6&lt;/code&gt;，并使用&lt;code&gt;0.8.6&lt;/code&gt; 进行构建！ &lt;/p&gt;

&lt;p&gt;换句话说，当前项目将会一直使用 &lt;code&gt;0.8.X&lt;/code&gt; 版本的 rand 包（&lt;code&gt;其中，X &amp;gt;= 5&lt;/code&gt;） ，直到你手动升级至其他版本。&lt;/p&gt;
&lt;h3 id="9. 生成一个随机数"&gt;9. 生成一个随机数&lt;/h3&gt;
&lt;p&gt;让我们开始使用 rand 来生成一个数字来猜测。下一步是更新 src/main.rs，程序清单如下：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Rng&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Guess the number!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;secret_number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;thread_rng&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.gen_range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"The secret number is: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;secret_number&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Please input your guess."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;guess&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;stdin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.read_line&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;guess&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to read line"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"You guessed: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;guess&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;首先，我们使用 &lt;code&gt;use rand::Rng&lt;/code&gt; 引入了 &lt;code&gt;rand::Rng&lt;/code&gt;，这里的&lt;code&gt;Rng&lt;/code&gt; 是一个特征（trait），它定义了随机数生成器需要实现的方法集合。&lt;/p&gt;

&lt;p&gt;接着，又增加了另外一行代码：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;secret_number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;thread_rng&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.gen_range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;rand::thread_rng()&lt;/code&gt; 会返回一个特定的随机数生成器，该生成器位于本地线程空间，并通过操作系统获得随机数种子。这个生成器又调用其方法 &lt;code&gt;gen_range&lt;/code&gt;，该方法是在刚刚引入的&lt;code&gt;Rng&lt;/code&gt; trait 中定义的方法，它接收两个数字作为参数，并生成一个范围在两者之间的随机数。&lt;/p&gt;

&lt;p&gt;现在用 &lt;code&gt;cargo run&lt;/code&gt; 运行程序，每次都会获得一个 1 到 100 之间的保密数字！&lt;/p&gt;
&lt;h3 id="10. 比较猜测数字与保密数字"&gt;10. 比较猜测数字与保密数字&lt;/h3&gt;
&lt;p&gt;现在，我们有了一个随机生成的保密数字，还有一个用户输入的猜测数字。接下来，我们编写比较的代码。&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Rng&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;cmp&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Ordering&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// --snip--&lt;/span&gt;

    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"You guessed: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;guess&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;guess&lt;/span&gt;&lt;span class="nf"&gt;.cmp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;secret_number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nn"&gt;Ordering&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Less&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Too small!"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nn"&gt;Ordering&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Greater&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Too big!"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nn"&gt;Ordering&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Equal&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"You win!"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在这里，我们引用了&lt;code&gt;std::cmp::Ordering&lt;/code&gt;类型，该类型有&lt;code&gt;Less/Greater/Equal&lt;/code&gt;三个变体，通过表示比较两个数字的结果。&lt;code&gt;guess&lt;/code&gt; 的 &lt;code&gt;cmp&lt;/code&gt;用于与&lt;code&gt;guess&lt;/code&gt;进行比较，返回的类型就是&lt;code&gt;Ordering&lt;/code&gt;。 &lt;code&gt;match&lt;/code&gt;对比较的结果进行决判，分别就三种变体做不同的处理，由于一共只有三种可能的情况，所以不管是哪一种，都会被&lt;code&gt;math&lt;/code&gt;处理掉。&lt;/p&gt;

&lt;p&gt;运行上面的程序，会得到如下的错误：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;error[E0308]: mismatched types
   &lt;span class="nt"&gt;--&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; src/main.rs:21:21
    |
21  |     match guess.cmp&lt;span class="o"&gt;(&lt;/span&gt;&amp;amp;secret_number&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    |                 &lt;span class="nt"&gt;---&lt;/span&gt; ^^^^^^^^^^^^^^ expected &lt;span class="sb"&gt;`&lt;/span&gt;&amp;amp;String&lt;span class="sb"&gt;`&lt;/span&gt;, found &lt;span class="sb"&gt;`&lt;/span&gt;&amp;amp;&lt;span class="o"&gt;{&lt;/span&gt;integer&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;
    |                 |
    |                 arguments to this method are incorrect
    |
    &lt;span class="o"&gt;=&lt;/span&gt; note: expected reference &lt;span class="sb"&gt;`&lt;/span&gt;&amp;amp;String&lt;span class="sb"&gt;`&lt;/span&gt;
               found reference &lt;span class="sb"&gt;`&lt;/span&gt;&amp;amp;&lt;span class="o"&gt;{&lt;/span&gt;integer&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;该错误的核心原因是：类型不匹配！因为 &lt;code&gt;guess&lt;/code&gt;的类型是 String，而 &lt;code&gt;secret_number&lt;/code&gt;的类型是整形 (i32)。为了能正常进行比较，需要将程序中读取的输入从 String 转换成数值类型。
修改代码如下：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;
&lt;span class="c1"&gt;// --snip--&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;guess&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;stdin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;.read_line&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;guess&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;.expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to read line"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;guess&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;guess&lt;/span&gt;&lt;span class="nf"&gt;.trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.parse&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Please type a number!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"You guessed: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;guess&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;guess&lt;/span&gt;&lt;span class="nf"&gt;.cmp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;secret_number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;Ordering&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Less&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Too small!"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nn"&gt;Ordering&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Greater&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Too big!"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nn"&gt;Ordering&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Equal&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"You win!"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;增加的代码行是：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let guess: u32 = guess.trim().parse().expect("Please type a number!");
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们创建一个名为 的变量&lt;code&gt;guess&lt;/code&gt;。但是等等，程序不是已经有一个名为 的变量&lt;code&gt;guess&lt;/code&gt;吗？确实如此，但有益的是，Rust 允许我们用一个新的值来掩盖之前的值。我们可以重用&lt;code&gt;guess&lt;/code&gt; 变量名，而不是强迫我们创建两个唯一的变量，例如 &lt;code&gt;guess_str&lt;/code&gt;和&lt;code&gt;guess&lt;/code&gt; 。&lt;/p&gt;

&lt;p&gt;我们将这个新变量绑定到表达式&lt;code&gt;guess.trim().parse()&lt;/code&gt;。表达式中的是指包含输入作为字符串&lt;code&gt;guess&lt;/code&gt; 的原始变量。&lt;code&gt;guess&lt;/code&gt;实例上的&lt;code&gt;trim&lt;/code&gt;方法将消除字符串开头和结尾的任何空格，我们必须这样做才能将&lt;code&gt;字符串&lt;/code&gt;与&lt;code&gt;u32&lt;/code&gt;只能包含数字数据的 进行比较。用户必须按 &lt;code&gt;Enter&lt;/code&gt;来满足&lt;code&gt;read_line&lt;/code&gt;并输入他们的猜测，这会在字符串中添加一个换行符。例如，如果用户输入&lt;code&gt;5&lt;/code&gt;并按下回车，&lt;code&gt;guess&lt;/code&gt;看起来像这样&lt;code&gt;5\n&lt;/code&gt;(“\n 代表换行符”）。（在 Windows 上，按&lt;code&gt;enter&lt;/code&gt;会导致回车和换行，即：&lt;code&gt;\r\n&lt;/code&gt;）。该&lt;code&gt;trim方法&lt;/code&gt;消除&lt;code&gt;\n或\r\n&lt;/code&gt; 。&lt;/p&gt;

&lt;p&gt;该&lt;code&gt;parse&lt;/code&gt;方法仅适用于可以逻辑转换为数字的字符，因此很容易导致错误。例如，如果字符串包含&lt;code&gt;A👍%&lt;/code&gt;，则无法将其转换为数字。因为它可能会失败，所以该&lt;code&gt;parse&lt;/code&gt;方法返回一个&lt;code&gt;Result&lt;/code&gt;类型，就像该&lt;code&gt;read_line&lt;/code&gt; 方法所做的一样。我们将再次使用&lt;code&gt;Result&lt;/code&gt;的&lt;code&gt;expect&lt;/code&gt;方法以同样的方式处理。如果由于无法从字符串中创建数字，则该调用将使游戏崩溃并打印我们给它的消息。&lt;/p&gt;

&lt;p&gt;让我们现在运行程序！&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;cargo run
   Compiling guessing_game v0.1.0 &lt;span class="o"&gt;(&lt;/span&gt;/Users/someone/rust_projects/guessing_game&lt;span class="o"&gt;)&lt;/span&gt;
    Finished &lt;span class="sb"&gt;`&lt;/span&gt;dev&lt;span class="sb"&gt;`&lt;/span&gt; profile &lt;span class="o"&gt;[&lt;/span&gt;unoptimized + debuginfo] target&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;1.40s
     Running &lt;span class="sb"&gt;`&lt;/span&gt;target/debug/guessing_game&lt;span class="sb"&gt;`&lt;/span&gt;
Guess the number!
The secret number is: 51
Please input your guess.
56
You guessed: 56
Too big!
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;好的！即使在猜测之前添加了空格，程序仍然认为用户猜了 56。运行程序几次以验证不同类型输入的不同行为：正确猜数字，猜一个太高的数字，并猜测一个太低的数字。&lt;/p&gt;

&lt;p&gt;我们现在已经完成了大部分游戏，但用户只能进行一次猜测。让我们通过添加一个循环来改变它！&lt;/p&gt;
&lt;h3 id="11. 使用循环允许多个猜测"&gt;11. 使用循环允许多个猜测&lt;/h3&gt;
&lt;p&gt;loop 关键字创建一个无限循环。我们将添加一个循环，让用户有更多机会猜测数字：
修改&lt;code&gt;src/main.rs&lt;/code&gt;中的代码如下：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;    &lt;span class="c1"&gt;// --snip--&lt;/span&gt;

    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"The secret number is: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;secret_number&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;loop&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Please input your guess."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// --snip--&lt;/span&gt;

        &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;guess&lt;/span&gt;&lt;span class="nf"&gt;.cmp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;secret_number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nn"&gt;Ordering&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Less&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Too small!"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="nn"&gt;Ordering&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Greater&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Too big!"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="nn"&gt;Ordering&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Equal&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"You win!"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如您所见，我们已将猜测输入提示中的所有内容移至循环中。该程序现在将永远要求另一个猜测，这实际上引入了一个新问题：玩家永远都没有办法正常地结束游戏了。用户要退出程序，只能通过 &lt;code&gt;Ctrl + C&lt;/code&gt;之类的快捷键强行终止程序。&lt;/p&gt;
&lt;h3 id="12. 在猜测成功时优雅地退出"&gt;12. 在猜测成功时优雅地退出&lt;/h3&gt;
&lt;p&gt;现在让我们的程序增加一条 &lt;code&gt;break&lt;/code&gt; 语句，使得玩家在猜对数字后能够正常退出游戏。&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;        &lt;span class="c1"&gt;// --snip--&lt;/span&gt;
        &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;guess&lt;/span&gt;&lt;span class="nf"&gt;.cmp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;secret_number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nn"&gt;Ordering&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Less&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Too small!"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="nn"&gt;Ordering&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Greater&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Too big!"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="nn"&gt;Ordering&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Equal&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"You win!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="13. 处理非法输入"&gt;13. 处理非法输入&lt;/h3&gt;
&lt;p&gt;目前的程序，当玩家输入一个非数字时，程序会崩溃，原因是程序没有对&lt;code&gt;字符串类型在转换成数字类型失败&lt;/code&gt;的情况作处理。为了进一步细化游戏的行为，而不是在用户输入非数字时使程序崩溃，让我们让游戏忽略非数字，以便用户继续猜测。修改代码如下：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;
&lt;span class="c1"&gt;// --snip--&lt;/span&gt;

&lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;stdin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;.read_line&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;guess&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;.expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to read line"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;guess&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;guess&lt;/span&gt;&lt;span class="nf"&gt;.trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.parse&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"You guessed: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;guess&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// --snip--&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们从一个&lt;code&gt;expect&lt;/code&gt;调用切换到一个&lt;code&gt;match&lt;/code&gt;表达式，从一个错误崩溃转移到处理错误。请记住，它&lt;code&gt;parse&lt;/code&gt;返回一个&lt;code&gt;Result&lt;/code&gt; 类型，并且&lt;code&gt;Result&lt;/code&gt;是一个具有&lt;code&gt;Ok&lt;/code&gt;或&lt;code&gt;Err&lt;/code&gt; 变体的枚举。我们在 &lt;code&gt;match&lt;/code&gt;这里使用表达式，就像我们对&lt;code&gt;cmp&lt;/code&gt;方法的&lt;code&gt;Ordering&lt;/code&gt;结果所做的那样。如果出现&lt;code&gt;Err&lt;/code&gt;这种情况，通过 &lt;code&gt;continue&lt;/code&gt; 跳到&lt;code&gt;loop&lt;/code&gt;的开始，让用户重新输入。&lt;/p&gt;

&lt;p&gt;很好，现在不管玩家输入什么，都不会崩溃了！&lt;/p&gt;
&lt;h3 id="14. 完整的代码"&gt;14. 完整的代码&lt;/h3&gt;
&lt;p&gt;等等！回想一下，程序仍在打印保密数字。这对测试很有效，但它破坏了游戏。让我们删除那个输出保密号码的&lt;code&gt;println!&lt;/code&gt;吧。&lt;/p&gt;

&lt;p&gt;完成代码如下：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Rng&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;cmp&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Ordering&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Guess the number!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;secret_number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;thread_rng&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.gen_range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;loop&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Please input your guess."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;guess&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;stdin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="nf"&gt;.read_line&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;guess&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;.expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to read line"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;guess&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;guess&lt;/span&gt;&lt;span class="nf"&gt;.trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.parse&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"You guessed: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;guess&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;guess&lt;/span&gt;&lt;span class="nf"&gt;.cmp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;secret_number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nn"&gt;Ordering&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Less&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Too small!"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="nn"&gt;Ordering&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Greater&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Too big!"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="nn"&gt;Ordering&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Equal&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"You win!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="总结"&gt;总结&lt;/h2&gt;
&lt;p&gt;通过对&lt;code&gt;Hello,world!&lt;/code&gt;、&lt;code&gt;Hello,cargo!&lt;/code&gt;、&lt;code&gt;猜数游戏&lt;/code&gt;讲解，我们学会了如何使用 &lt;code&gt;Cargo&lt;/code&gt; 来创建新项目、管理项目中的依赖，并接触了&lt;code&gt;let&lt;/code&gt;、&lt;code&gt;match&lt;/code&gt;、&lt;code&gt;方法&lt;/code&gt;、&lt;code&gt;关联函数&lt;/code&gt;及&lt;code&gt;包（crate）的使用&lt;/code&gt;等概念。但是，我们还没有迈开&lt;code&gt;学习Rust语言旅途&lt;/code&gt;的第一步，现在顶多只是做一些热身动作。下一篇，我们正式出发，你准备好了吗？&lt;/p&gt;</description>
      <author>james</author>
      <pubDate>Fri, 24 May 2024 10:49:41 +0800</pubDate>
      <link>https://soldev.cn/topics/8</link>
      <guid>https://soldev.cn/topics/8</guid>
    </item>
    <item>
      <title>工欲善其事，必先利其器 —— Rust 开发环境之搭建</title>
      <description>&lt;blockquote&gt;
&lt;p&gt;子曰：工欲善其事，必先利其器。居是邦也，事其大夫之贤者，友其士之仁者。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;本章是《探幽 Rust 语言》系列文章的开篇之文，主要包括 Rust 语言简介、环境安装及相关配置的内容。重点是搭建开发环境，开发环境是修练 Rust 大法的练兵场和屠龙宝刀。&lt;/p&gt;
&lt;h2 id="Rust简介"&gt;Rust 简介&lt;/h2&gt;
&lt;p&gt;&lt;img src="/uploads/photo/james/57310533-04ed-48f9-a3f0-0bb0990e6843.jpg!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Rust 语言最初由 Graydon Hoare 在 Mozilla Research 开发，自 2006 年起得到 Mozilla 的支持。Rust 第一个有版本号的 Rust 编译器是 2012 年 1 月发布的，2015 年 5 月 15 日发布了第一个稳定版本 1.0 版。其设计目标是成为 C++ 的替代品，旨在解决 C/C++ 语言安全性和并发性的问题。与&lt;code&gt;Golang&lt;/code&gt;一样，被称为是&lt;code&gt;21世纪的C语言&lt;/code&gt;，不过究竟谁才是 &lt;code&gt;C语言&lt;/code&gt; 真正的替代者，我们只能拭目以待 ^-^ ！&lt;/p&gt;
&lt;h3 id="Rust特点"&gt;Rust 特点&lt;/h3&gt;
&lt;p&gt;能想到的用于描述 Rust 特点的关键字，大致有&lt;code&gt;静态强类型&lt;/code&gt;、&lt;code&gt;极其强大的编译器&lt;/code&gt;、&lt;code&gt;零成本抽象&lt;/code&gt;、&lt;code&gt;无垃圾回收器（GC）&lt;/code&gt;、&lt;code&gt;零运行时（或者说运行时很小）&lt;/code&gt;等等，其中的具体意思后续的文章中会有专门的介绍。Rust 同时支持&lt;code&gt;包括函数式、过程式、面向对象&lt;/code&gt;在内的多种编程范式。总之，它一种相当优秀的现代编程语言。&lt;/p&gt;

&lt;p&gt;让我们来看看 Rust 官方的介绍（&amp;lt;&lt;a href="https://www.rust-lang.org/zh-CN/" rel="nofollow" target="_blank"&gt;https://www.rust-lang.org/zh-CN/&lt;/a&gt;）
&lt;img src="/uploads/photo/james/a154128e-2912-4eca-8866-910e7d63722f.jpg!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;其主要的特点有：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;高性能&lt;/strong&gt;：Rust 速度惊人且内存利用率极高。由于没有运行时和垃圾回收，它能够胜任对性能要求特别高的服务，可以在嵌入式设备上运行，还能轻松和其他语言集成。。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;可靠性&lt;/strong&gt;：Rust 丰富的类型系统和所有权模型保证了内存安全和线程安全，让您在编译期就能够消除各种各样的错误。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;生产力&lt;/strong&gt;：Rust 拥有出色的文档、友好的编译器和清晰的错误提示信息，还集成了一流的工具 —— 包管理器和构建工具，智能地自动补全和类型检验的多编辑器支持，以及自动格式化代码等等。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="Rust的使用场景"&gt;Rust 的使用场景&lt;/h3&gt;
&lt;p&gt;&lt;img src="/uploads/photo/james/398c5b5b-923e-490d-8bd4-4b7558269bb6.jpg!large" title="" alt=""&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;传统命令行程序&lt;/strong&gt;：Rust 编译器可以直接生成目标可执行程序，不需要任何解释程序。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Web 应用&lt;/strong&gt;：Rust 可以被编译成 WebAssembly，WebAssembly 是一种 JavaScript 的高效替代品。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;网络服务器&lt;/strong&gt;：Rust 用极低的资源消耗做到安全高效，且具备很强的大规模并发处理能力，十分适合开发普通或极端的服务器程序。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;嵌入式开发&lt;/strong&gt;：Rust 同时具有 JavaScript 一般的高效开发语法和 C 语言的执行效率，支持底层平台的开发。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;系统编程&lt;/strong&gt;：如操作系统、文件系统、游戏引擎和浏览器的组件。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;跨平台应用&lt;/strong&gt;：能够编写一次代码，运行在多种操作系统和平台上。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="安装Rust开发环境"&gt;安装 Rust 开发环境&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;本文仅介绍&lt;code&gt;类unix操作系统&lt;/code&gt;的安装，&lt;code&gt;Windows等其他操作系统&lt;/code&gt;请参照其他相关文档。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;具体的安装过程参考官方的文档：&lt;a href="https://www.rust-lang.org/zh-CN/tools/install" rel="nofollow" target="_blank"&gt;https://www.rust-lang.org/zh-CN/tools/install&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="Rust中的版本"&gt;Rust 中的版本&lt;/h3&gt;
&lt;p&gt;Rust 有三类版本：稳定版 (stable)、公测版 (beta) 和 nightly 版，分别对应的是 stable 分支、beta 分支和 master 分支。&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;稳定版（stable）：对于大多数 Rust 开发者而言，最新的稳定版 (stable) 是最好和最明智的选择&lt;/li&gt;
&lt;li&gt;Beta 版 contributor：或是想提前尝鲜下一个稳定版新特性的开发人员，可以临时使用 beta 版本；&lt;/li&gt;
&lt;li&gt;Nightly 版：主要针对的也是 contributor，或是想临时尝鲜最新不稳定功能特性的开发人员。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="使用rustup安装最新stable版本"&gt;使用 rustup 安装最新 stable 版本&lt;/h3&gt;
&lt;p&gt;Rust 提供了统一的安装、管理、升级、卸载工具 —— &lt;code&gt;rustup&lt;/code&gt;。其安装命令：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--proto&lt;/span&gt; &lt;span class="s1"&gt;'=https'&lt;/span&gt; &lt;span class="nt"&gt;--tlsv1&lt;/span&gt;.2 &lt;span class="nt"&gt;-sSf&lt;/span&gt; https://sh.rustup.rs | sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;执行上面的命令，需要先安装好&lt;code&gt;curl&lt;/code&gt;，它会从&lt;code&gt;https://sh.rustup.rs&lt;/code&gt;下载&lt;code&gt;rustup-init.sh&lt;/code&gt;脚本文件，并在命令终端中执行里面的命令。如果一切顺利，最后会终端上打印出类似如下的信息：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Rust is installed now. Great!

To get started you may need to restart your current shell.
This would reload your PATH environment variable to include
Cargo&lt;span class="s1"&gt;'s bin directory ($HOME/.cargo/bin).

To configure your current shell, you need to source
the corresponding env file under $HOME/.cargo.

This is usually done by running one of the following (note the leading DOT):
. "$HOME/.cargo/env"            # For sh/bash/zsh/ash/dash/pdksh
source "$HOME/.cargo/env.fish"  # For fish
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;参照上面的提示，根据系统的 shell 类型，配置 shell 的环境变量。比如：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$.&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.cargo/env"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;执行完上面的命令后，查看一下安装的 rustup 版本：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;rustup &lt;span class="nt"&gt;--version&lt;/span&gt;
rustup 1.27.0 &lt;span class="o"&gt;(&lt;/span&gt;bbb9276d2 2024-03-08&lt;span class="o"&gt;)&lt;/span&gt;
info: This is the version &lt;span class="k"&gt;for &lt;/span&gt;the rustup toolchain manager, not the rustc compiler.
info: The currently active &lt;span class="sb"&gt;`&lt;/span&gt;rustc&lt;span class="sb"&gt;`&lt;/span&gt; version is &lt;span class="sb"&gt;`&lt;/span&gt;rustc 1.78.0 &lt;span class="o"&gt;(&lt;/span&gt;9b00956e5 2024-04-29&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;恭喜你！至此，我们已经把 rust 最新的 stable（即 1.78.0）版本安装好了。&lt;/p&gt;

&lt;p&gt;还可以执行下面命令，查看 rust 究竟安装在哪个目录下。&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;which rustup
/Users/someone/.cargo/bin/rustup
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="安装与使用指定版本"&gt;安装与使用指定版本&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;安装
&lt;code&gt;shell
$ rustup install 1.76.0
&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;使用
&lt;code&gt;shell
$ rustc +1.76.0 -V
rustc 1.76.0 (07dca489a 2024-02-04)
$ cargo +1.76.0 --version
cargo 1.76.0 (c84b36747 2024-01-18)
$ cargo +1.66.0 --version
error: toolchain '1.66.0-x86_64-apple-darwin' is not installed
&lt;/code&gt;&lt;br&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="查看已经安装的rust工具链"&gt;查看已经安装的 rust 工具链&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;rustup show

Default host: x86_64-apple-darwin
rustup home:  /Users/someone/.rustup

installed toolchains
&lt;span class="nt"&gt;--------------------&lt;/span&gt;

stable-x86_64-apple-darwin &lt;span class="o"&gt;(&lt;/span&gt;default&lt;span class="o"&gt;)&lt;/span&gt;
nightly-2021-03-11-x86_64-apple-darwin
nightly-2021-10-21-x86_64-apple-darwin
nightly-2022-03-14-x86_64-apple-darwin
nightly-2022-07-11-x86_64-apple-darwin
nightly-2023-04-25-x86_64-apple-darwin
nightly-2024-02-04-x86_64-apple-darwin
nightly-x86_64-apple-darwin
1.76.0-x86_64-apple-darwin
solana

&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="安装后的几个相关目录解析"&gt;安装后的几个相关目录解析&lt;/h3&gt;
&lt;p&gt;有两个目录需要关注，一个是 &lt;code&gt;$HOME/.cargo&lt;/code&gt;(安装成功后会把.cargo/bin 加入到了 PATH 环境变量中)，另一个是&lt;code&gt;$HOME/.rustup&lt;/code&gt;。&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;$HOME/.cargo&lt;/code&gt;目录：&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;
tree &lt;span class="nt"&gt;-F&lt;/span&gt; &lt;span class="nt"&gt;-L&lt;/span&gt; 2 .cargo
.cargo
├── bin/
│   ├── anchor&lt;span class="k"&gt;*&lt;/span&gt;
│   ├── avm&lt;span class="k"&gt;*&lt;/span&gt;
│   ├── bindgen&lt;span class="k"&gt;*&lt;/span&gt;
│   ├── cargo&lt;span class="k"&gt;*&lt;/span&gt;
│   ├── cargo-add&lt;span class="k"&gt;*&lt;/span&gt;
│   ├── cargo-clippy&lt;span class="k"&gt;*&lt;/span&gt;
│   ├── cargo-contract&lt;span class="k"&gt;*&lt;/span&gt;
│   ├── cargo-dylint&lt;span class="k"&gt;*&lt;/span&gt;
│   ├── cargo-fmt&lt;span class="k"&gt;*&lt;/span&gt;
│   ├── cargo-miri&lt;span class="k"&gt;*&lt;/span&gt;
│   ├── cargo-remote&lt;span class="k"&gt;*&lt;/span&gt;
│   ├── cargo-rm&lt;span class="k"&gt;*&lt;/span&gt;
│   ├── cargo-set-version&lt;span class="k"&gt;*&lt;/span&gt;
│   ├── cargo-upgrade&lt;span class="k"&gt;*&lt;/span&gt;
│   ├── clippy-driver&lt;span class="k"&gt;*&lt;/span&gt;
│   ├── dylint-link&lt;span class="k"&gt;*&lt;/span&gt;
│   ├── grcov&lt;span class="k"&gt;*&lt;/span&gt;
│   ├── kickstart&lt;span class="k"&gt;*&lt;/span&gt;
│   ├── mdbook&lt;span class="k"&gt;*&lt;/span&gt;
│   ├── mini-redis-cli&lt;span class="k"&gt;*&lt;/span&gt;
│   ├── mini-redis-server&lt;span class="k"&gt;*&lt;/span&gt;
│   ├── rls&lt;span class="k"&gt;*&lt;/span&gt;
│   ├── rust-analyzer&lt;span class="k"&gt;*&lt;/span&gt;
│   ├── rust-gdb&lt;span class="k"&gt;*&lt;/span&gt;
│   ├── rust-gdbgui&lt;span class="k"&gt;*&lt;/span&gt;
│   ├── rust-lldb&lt;span class="k"&gt;*&lt;/span&gt;
│   ├── rustc&lt;span class="k"&gt;*&lt;/span&gt;
│   ├── rustdoc&lt;span class="k"&gt;*&lt;/span&gt;
│   ├── rustfmt&lt;span class="k"&gt;*&lt;/span&gt;
│   └── rustup&lt;span class="k"&gt;*&lt;/span&gt;
├── config
├── credentials
├── &lt;span class="nb"&gt;env&lt;/span&gt;
├── git/
│   ├── CACHEDIR.TAG
│   ├── checkouts/
│   └── db/
└── registry/
    ├── CACHEDIR.TAG
    ├── cache/
    ├── index/
    └── src/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;bin 目录下存放了 rust 开发中常用的 cli，比如 cargo、rustc、rustdoc、rustup 等。&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;code&gt;$HOME/.rustup&lt;/code&gt;目录&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;
tree &lt;span class="nt"&gt;-F&lt;/span&gt; &lt;span class="nt"&gt;-L&lt;/span&gt; 2 .rustup
.rustup
├── downloads/
├── settings.toml
├── tmp/
├── toolchains/
│   ├── 1.76.0-x86_64-apple-darwin/
│   ├── nightly-2021-03-11-x86_64-apple-darwin/
│   ├── nightly-2021-10-21-x86_64-apple-darwin/
│   ├── nightly-2022-03-14-x86_64-apple-darwin/
│   ├── nightly-2022-07-11-x86_64-apple-darwin/
│   ├── nightly-2023-04-25-x86_64-apple-darwin/
│   ├── nightly-2024-02-04-x86_64-apple-darwin/
│   ├── nightly-x86_64-apple-darwin/
│   ├── solana -&amp;gt; /Users/someone/solana-release/bin/sdk/sbf/dependencies/platform-tools/rust
│   └── stable-x86_64-apple-darwin/
└── update-hashes/
    ├── 1.76.0-x86_64-apple-darwin
    ├── nightly-2021-03-11-x86_64-apple-darwin
    ├── nightly-2021-10-21-x86_64-apple-darwin
    ├── nightly-2022-03-14-x86_64-apple-darwin
    ├── nightly-2022-07-11-x86_64-apple-darwin
    ├── nightly-2023-04-25-x86_64-apple-darwin
    ├── nightly-2024-02-04-x86_64-apple-darwin
    ├── nightly-x86_64-apple-darwin
    └── stable-x86_64-apple-darwin
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;setting.toml&lt;/code&gt;是 rust 的配置文件，其内容如下：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; .rustup/settings.toml
default_host_triple &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"x86_64-apple-darwin"&lt;/span&gt;
default_toolchain &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"stable-x86_64-apple-darwin"&lt;/span&gt;
profile &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"default"&lt;/span&gt;
version &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"12"&lt;/span&gt;

&lt;span class="o"&gt;[&lt;/span&gt;overrides]
&lt;span class="s2"&gt;"/Users/someone/Desktop/Demo"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"nightly-x86_64-apple-darwin"&lt;/span&gt;
&lt;span class="s2"&gt;"/Users/someone/deeper-chain"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"nightly-2021-10-21-x86_64-apple-darwin"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;上面的&lt;code&gt;default_toolchain&lt;/code&gt;，指示了当前默认使用的工具链版本&lt;code&gt;stable-x86_64-apple-darwin&lt;/code&gt;，即目标机器为 apple，处理器架构为 x86，rust 的版本为 stable。&lt;/li&gt;
&lt;li&gt;上面的&lt;code&gt;[overrides]&lt;/code&gt;，是对具体的项目的版本进行定制化重写。比如对位于&lt;code&gt;/Users/someone/Desktop/Demo&lt;/code&gt;的项目而言，其目标机器为 apple，处理器架构为 x86，rust 的版本为 nightly。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;toolchains&lt;/code&gt;目录，它下面存放了安装到本地的所有版本的 toolchain。&lt;/p&gt;
&lt;h2 id="使用特定版本构建项目"&gt;使用特定版本构建项目&lt;/h2&gt;&lt;h3 id="使用 rustup  override"&gt;使用 rustup  override&lt;/h3&gt;
&lt;p&gt;构建项目时所用的版本，是由&lt;code&gt;.rustup/settings.toml&lt;/code&gt;中的&lt;code&gt;default_toolchain&lt;/code&gt;指定的，通常情况下都是最新的 stable 版本。对于要使用特定版本进行构建的 rust 项目，我们可以通过&lt;code&gt;rustup  override&lt;/code&gt;来指定版本号。下面是一个例子：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;cargo new hello &lt;span class="c"&gt;#用cargo创建一个项目，项目名称为hello&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;rustc &lt;span class="nt"&gt;-V&lt;/span&gt; &lt;span class="c"&gt;#查看rustc的版本，当前最新的版本为 1.78.0&lt;/span&gt;
rustc 1.78.0 &lt;span class="o"&gt;(&lt;/span&gt;9b00956e5 2024-04-29&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;hello
&lt;span class="nv"&gt;$ &lt;/span&gt;rustup override &lt;span class="nb"&gt;set &lt;/span&gt;1.76.0 &lt;span class="c"&gt;#指定当前项目的rust版本为1.76.0&lt;/span&gt;
info: override toolchain &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="s1"&gt;'/Users/someone/Desktop/rust_study/hello'&lt;/span&gt; &lt;span class="nb"&gt;set &lt;/span&gt;to &lt;span class="s1"&gt;'1.76.0-x86_64-apple-darwin'&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们用 cargo 创建了一个新的 hello 项目，并该项目下，指定其使用 1.76.0 版本进行构建。
现在，在该项目下执行 rustc 查看版本：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;rustc &lt;span class="nt"&gt;-V&lt;/span&gt;
rustc 1.76.0 &lt;span class="o"&gt;(&lt;/span&gt;07dca489a 2024-02-04&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;再跳到项目之外的目录，再次查看 rustc 版本：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~
&lt;span class="nv"&gt;$ &lt;/span&gt;rustc &lt;span class="nt"&gt;-V&lt;/span&gt;
rustc 1.78.0 &lt;span class="o"&gt;(&lt;/span&gt;9b00956e5 2024-04-29&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;rustc override 的原理其实是在 &lt;code&gt;$HOME/.rustup/settings.toml&lt;/code&gt;文件中添加了一些内容：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; .rustup/settings.toml
default_host_triple &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"x86_64-apple-darwin"&lt;/span&gt;
default_toolchain &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"stable-x86_64-apple-darwin"&lt;/span&gt;
profile &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"default"&lt;/span&gt;
version &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"12"&lt;/span&gt;

&lt;span class="o"&gt;[&lt;/span&gt;overrides]
&lt;span class="s2"&gt;"/Users/someone/Desktop/Demo"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"nightly-x86_64-apple-darwin"&lt;/span&gt;
&lt;span class="s2"&gt;"/Users/someone/Desktop/rust_study/hello"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1.76.0-x86_64-apple-darwin"&lt;/span&gt;
&lt;span class="s2"&gt;"/Users/someone/deeper-chain"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"nightly-2021-10-21-x86_64-apple-darwin"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如上所示，在 [overrides] 的第二行，新增了一条规则，指定 hello 项目的构建工具链版本为特定版本。
可以使用&lt;code&gt;rustup override unset&lt;/code&gt;取消重载效果：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;rustup override &lt;span class="nb"&gt;unset&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="使用rust-toolchain.toml方件"&gt;使用 rust-toolchain.toml 方件&lt;/h3&gt;
&lt;p&gt;使用上面的&lt;code&gt;rustup override set&lt;/code&gt;命令指定工具链的版本，致使项目的配置信息与&lt;code&gt;.rustup/settings.toml&lt;/code&gt;紧耦合在一起，不利于通过 git 进行协同合作。Rust 提供了另一种方法，即在项目根目录下放置一个名为&lt;code&gt;rust-toolchain.toml&lt;/code&gt;配置文件，其大致内容如下：&lt;/p&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[toolchain]&lt;/span&gt;
&lt;span class="py"&gt;channel&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"1.76.0"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;取消掉之前的设置：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;rustup override &lt;span class="nb"&gt;unset&lt;/span&gt; &lt;span class="c"&gt;# 在项目的目录下执行&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后&lt;code&gt;rust-toolchain.toml&lt;/code&gt;就生效了：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;rustc &lt;span class="nt"&gt;-V&lt;/span&gt; &lt;span class="c"&gt;#在hello项目的目录下执行&lt;/span&gt;
rustc 1.76.0 &lt;span class="o"&gt;(&lt;/span&gt;07dca489a 2024-02-04&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可见，工具链的 override 存在着优先级，Rust 规定版本 override 的优先级顺序由高到低依次是：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;plus 语法：&lt;code&gt;rustc +1.76.0 -V&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;RUSTUP_TOOLCHAIN&lt;/code&gt; 环境变量 (default: none)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;rustup override set&lt;/code&gt; 命令&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rust-toolchain.toml&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;默认toolchain&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="几个常用命令"&gt;几个常用命令&lt;/h2&gt;
&lt;p&gt;与搭建开发环境相关的几个常用命令，作为总结列出如下：&lt;/p&gt;
&lt;h3 id="更新"&gt;更新&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;rustup update
rustup update stable &lt;span class="c"&gt;#stable可替换为你想要的版本&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="安装指定版本"&gt;安装指定版本&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;rustup &lt;span class="nb"&gt;install &lt;/span&gt;seme_ver
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="卸载"&gt;卸载&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;rustup self uninstall
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="版本间切换"&gt;版本间切换&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;rustup default stable 
rustup default nightly
rustup default beta &lt;span class="c"&gt;#如果没有安装，切换的时候为安装&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="加速cargo"&gt;加速 cargo&lt;/h2&gt;
&lt;p&gt;cargo 是 Rust 的重要工具，其中的作用之一就是包管理（类似于 Golang 中的 go mod，Java 中的 Maven），在开发 rust 程序时，默认会从 crates.io 仓库中下载程序所依赖的包（crate)。由于众所周知的原因，速度可能会很慢。业界一种通常做法，是把 cargo 源配置为国内的镜像。以下是在类 unix 系统中的配置方法。&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;进入&lt;code&gt;$HOME/.cargo&lt;/code&gt;目录
&lt;code&gt;shell
$ cd $HOME/.cargo
&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;创建&lt;code&gt;config&lt;/code&gt;
&lt;code&gt;shell
$ touch config #如果没有该文件
&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;把如下所示的内容复制到&lt;code&gt;config&lt;/code&gt;中&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;
&lt;span class="o"&gt;[&lt;/span&gt;source.crates-io]
registry &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https://github.com/rust-lang/crates.io-index"&lt;/span&gt;

replace-with &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'ustc'&lt;/span&gt; &lt;span class="c"&gt;#根据需要替换你想要的镜像，比如：tuna、rustcc 等&lt;/span&gt;

&lt;span class="c"&gt;# 中国科学技术大学&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;source.ustc]
registry &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https://mirrors.ustc.edu.cn/crates.io-index"&lt;/span&gt;

&lt;span class="c"&gt;# 上海交通大学&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;source.sjtu]
registry &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https://mirrors.sjtug.sjtu.edu.cn/git/crates.io-index/"&lt;/span&gt;

&lt;span class="c"&gt;# 清华大学&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;source.tuna]
registry &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https://mirrors.tuna.tsinghua.edu.cn/git/crates.io-index.git"&lt;/span&gt;

&lt;span class="c"&gt;# 阿里云rustcc社区&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;source.rustcc]
registry &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https://code.aliyun.com/rustcc/crates.io-index.git"&lt;/span&gt;

&lt;span class="c"&gt;# 字节跳动&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;source.rsproxy]
registry &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https://rsproxy.cn/crates.io-index.git"&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="Rust在线练兵场"&gt;Rust 在线练兵场&lt;/h2&gt;
&lt;p&gt;如果你不想在本地安装开发环境，Rust 官方提供了一个在线练兵场以供你学习或体验。这个练兵场就是：&lt;a href="https://play.rust-lang.org/?version=stable&amp;amp;mode=debug&amp;amp;edition=2021" rel="nofollow" target="_blank" title=""&gt;Rust Playground&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/james/e8ec7d30-7cf4-4b56-ab51-c85825b23593.jpg!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;在这个 Playground 中，你可以选择 Rust 的版本（stable、beta、nightly）、编译模式（debug、release）、Rust 的版本；可以选择执行一些工具，比如 rustfmt；可以选择执行的命令：Run、Build、Test、MIR 等。&lt;/p&gt;
&lt;h2 id="编辑器与IDE"&gt;编辑器与 IDE&lt;/h2&gt;
&lt;p&gt;这一块没有具体的要求，可选择的范围也比较多。当前大致可选择的有：&lt;code&gt;Vscode&lt;/code&gt;、&lt;code&gt;Sublime Text&lt;/code&gt;、&lt;code&gt;Rustrover&lt;/code&gt;、&lt;code&gt;Eclipse&lt;/code&gt;、&lt;code&gt;Clion&lt;/code&gt;、&lt;code&gt;Vim&lt;/code&gt;等。
&lt;img src="/uploads/photo/james/dc931f24-7498-43b8-9593-bfb475e2c78a.jpg!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;具体选择哪一款，看自己的喜好。相关的配置，可自行参照对应产品的文档。
官方提供的链接：&lt;a href="https://www.rust-lang.org/zh-CN/tools" rel="nofollow" target="_blank" title=""&gt;用于开发 Rust 的 IDE&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="总结"&gt;总结&lt;/h2&gt;
&lt;p&gt;本文分别就&lt;code&gt;Rust简介、Rust开发环境的搭建、使用特定版本构建项目、加速cargo的配置&lt;/code&gt;等内容进行了系统的介绍，阅读完并按照本文的步骤操作下来，应能在本地成功地搭建好 Rust 开发环境。&lt;/p&gt;

&lt;p&gt;至此，我们有了修练 Rust 大法屠龙宝刀。只是，这把刀尚徒有其名。通往 Rust 殿堂的路，已在脚下。等待你的，有如狐如妖的诱惑，有&lt;code&gt;剪不断理还乱&lt;/code&gt;的情愁，也有&lt;code&gt;本来无一物,何处惹尘埃&lt;/code&gt;的通透与惬意。既然选择了，就不要轻言放弃。千磨万击还坚韧，任尔东南西北风。出发吧，少年！&lt;/p&gt;</description>
      <author>james</author>
      <pubDate>Sat, 18 May 2024 17:22:04 +0800</pubDate>
      <link>https://soldev.cn/topics/3</link>
      <guid>https://soldev.cn/topics/3</guid>
    </item>
  </channel>
</rss>
