<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://blog.bran-nie.cn/feed.xml" rel="self" type="application/atom+xml" /><link href="https://blog.bran-nie.cn/" rel="alternate" type="text/html" /><updated>2026-03-19T11:07:00+08:00</updated><id>https://blog.bran-nie.cn/feed.xml</id><title type="html">鹏程</title><subtitle>description</subtitle><author><name>Bran.nie</name></author><entry><title type="html">Rust 基础入门 - 04（复合类型 - 结构体）</title><link href="https://blog.bran-nie.cn/2023/11/13/rust-learn-base-05/" rel="alternate" type="text/html" title="Rust 基础入门 - 04（复合类型 - 结构体）" /><published>2023-11-13T00:00:00+08:00</published><updated>2023-11-13T00:00:00+08:00</updated><id>https://blog.bran-nie.cn/2023/11/13/rust-learn-base-05</id><content type="html" xml:base="https://blog.bran-nie.cn/2023/11/13/rust-learn-base-05/"><![CDATA[<p>这是 bran 学习 Rust 的笔记 &amp; 心得系列，目前是 Rust 基础入门 - 04（复合类型 - 结构体），刚开始学哦～ 加油！！！</p>]]></content><author><name>Bran.nie</name></author><category term="Rust" /><summary type="html"><![CDATA[Rust 基础入门 - 04（复合类型 - 结构体）]]></summary></entry><entry><title type="html">Rust 基础入门 - 04（复合类型 - 元组）</title><link href="https://blog.bran-nie.cn/2023/11/09/rust-learn-base-04/" rel="alternate" type="text/html" title="Rust 基础入门 - 04（复合类型 - 元组）" /><published>2023-11-09T00:00:00+08:00</published><updated>2023-11-09T00:00:00+08:00</updated><id>https://blog.bran-nie.cn/2023/11/09/rust-learn-base-04</id><content type="html" xml:base="https://blog.bran-nie.cn/2023/11/09/rust-learn-base-04/"><![CDATA[<p>这是 bran 学习 Rust 的笔记 &amp; 心得系列，目前是 Rust 基础入门 - 04（复合类型 - 元组），刚开始学哦～ 加油！！！</p>

<ul>
  <li>元组是有多种类型组合到一起形成的</li>
  <li>复合类型</li>
  <li>长度固定</li>
  <li>顺序固定</li>
  <li>（300， ‘1’, 3.4）</li>
  <li>可以通过模式匹配结构元组</li>
  <li><code class="language-plaintext highlighter-rouge">.</code>操作符也可以获取元组属性
    <ul>
      <li>使用下表访问</li>
      <li>索引从 0 开始</li>
    </ul>
  </li>
  <li>使用示例：
    <ul>
      <li>函数返回值</li>
    </ul>
  </li>
</ul>

<p>总结：总体来说，元组像是一个聚合类型，将多个变量聚合在一起，但不具有过于清晰的含义。</p>]]></content><author><name>Bran.nie</name></author><category term="Rust" /><summary type="html"><![CDATA[Rust 基础入门 - 04（复合类型 - 元组）]]></summary></entry><entry><title type="html">Rust 基础入门 - 03（复合类型 - 字符串切片）</title><link href="https://blog.bran-nie.cn/2023/10/27/rust-learn-base-03/" rel="alternate" type="text/html" title="Rust 基础入门 - 03（复合类型 - 字符串切片）" /><published>2023-10-27T00:00:00+08:00</published><updated>2023-10-27T00:00:00+08:00</updated><id>https://blog.bran-nie.cn/2023/10/27/rust-learn-base-03</id><content type="html" xml:base="https://blog.bran-nie.cn/2023/10/27/rust-learn-base-03/"><![CDATA[<p>这是 bran 学习 Rust 的笔记 &amp; 心得系列，目前是 Rust 基础入门 - 03（复合类型 - 字符串切片），刚开始学哦～ 加油！！！</p>

<blockquote>
  <p>行百里者半九十</p>
</blockquote>

<ul>
  <li>复合类型，顾名思义，复合类型是由其他类型组合而成的，最典型的就是结构体 struct 和枚举 enum</li>
</ul>

<h2 id="字符串">字符串</h2>

<blockquote>
  <p>在其他语言中，字符串往往是比较简单的（js 是的），但在 Rust 中，字符串是一个很多设计的类型</p>
</blockquote>

<h3 id="切片-slice">切片 Slice</h3>

<ul>
  <li>切片并不是 Rust 独有的概念，在 Go 语言中也是有的，它可以让我们引用<strong>集合中部分连续的元素序列</strong>，而不是整个序列</li>
</ul>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">s</span> <span class="o">=</span> <span class="nn">String</span><span class="p">::</span><span class="nf">from</span><span class="p">(</span><span class="s">"hello world"</span><span class="p">);</span>

<span class="k">let</span> <span class="n">hello</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">s</span><span class="p">[</span><span class="mi">0</span><span class="o">..</span><span class="mi">5</span><span class="p">];</span>
<span class="k">let</span> <span class="n">world</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">s</span><span class="p">[</span><span class="mi">6</span><span class="o">..</span><span class="mi">11</span><span class="p">];</span>
</code></pre></div></div>

<ul>
  <li>
    <p>创建切片的方法：使用方括号包括的一个序列：<strong>[开始索引..终止索引]</strong></p>

    <ul>
      <li><code class="language-plaintext highlighter-rouge">&amp;s[0..5]</code></li>
      <li>..（range 序列语法）</li>
    </ul>
  </li>
  <li>
    <p><strong>切片语法需要格外小心</strong>：切片的索引必须在字符之间的边界位置，也就是 UTF-8 字符的边界，如中文在 UTF-8 中占用三个字节，下面的代码就会崩溃</p>
  </li>
  <li>
    <div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code>   <span class="k">let</span> <span class="n">s</span> <span class="o">=</span> <span class="s">"中国人"</span><span class="p">;</span>
   <span class="k">let</span> <span class="n">a</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">s</span><span class="p">[</span><span class="mi">0</span><span class="o">..</span><span class="mi">2</span><span class="p">];</span>
   <span class="nd">println!</span><span class="p">(</span><span class="s">"{}"</span><span class="p">,</span><span class="n">a</span><span class="p">);</span>
  <span class="c1">// 因为我们只取 s 字符串的前两个字节，但是本例中每个汉字占用三个字节，因此没有落在边界处，也就是连 中 字都取不完整，此时程序会直接崩溃退出，如果改成 &amp;s[0..3]，则可以正常通过编译。 因此，当你需要对字符串做切片索引操作时，需要格外小心这一点</span>
</code></pre></div>    </div>
  </li>
  <li>当我们已经有了可变借用时，就无法再拥有不可变的借用</li>
</ul>

<h4 id="其他切片">其他切片</h4>

<ul>
  <li>因为切片的定义是：对集合的部分引用，因此，不仅字符串有切片，其他集合类型也有，如数组</li>
</ul>

<h3 id="字符串字面量是切片">字符串字面量是切片</h3>

<ul>
  <li>直接声明赋值一个 <code class="language-plaintext highlighter-rouge">let s = "hello";</code>s 的类型实际上是 字符串切片。</li>
</ul>

<h2 id="什么是字符串">什么是字符串？</h2>

<ul>
  <li>Rust 中的「字符」是 Unicdoe 类型，因此每个字符占据 4 个字节的内存空间</li>
  <li>字符串是 UTF-8 编码，也就是字符串中的字符所占用的字节数是变化的（1-4）（这样有助于大幅降低字符串所占用的内存空间）</li>
  <li>Rust 在语言级别，只有一种字符串类型：str，通常是以引用类型出现：&amp;str，也就是前面提到的字符串切片</li>
  <li>虽然语言级别只有 str 类型，但标准库中，还有多种不同用途的字符串类型，其中使用最广的是 <code class="language-plaintext highlighter-rouge">String</code> 类型</li>
  <li>str 类型是硬编码的可执行文件，也无法被修改</li>
  <li>String 是一个可增长、可改变且具有所有权的 UTF-8 编码字符串</li>
  <li>当 Rust 用户提到字符串时，往往指的就是 String 类型 和 &amp;str 字符串切片类型，这两个类型都是 UTF-8 编码</li>
</ul>

<h2 id="string-与-str-的转换">String 与 &amp;str 的转换</h2>

<ul>
  <li>从 &amp;str 类型生成 String 类型：<code class="language-plaintext highlighter-rouge">String::from("hello bran")</code>、<code class="language-plaintext highlighter-rouge">"hello bran".to_string()</code></li>
  <li>从 String 类型到 &amp;str 类型：取引用，<code class="language-plaintext highlighter-rouge">let s = String.from("hello"); let s1 = &amp;s[0..3]</code>
    <ul>
      <li>实际上这种灵活用法是因为 <code class="language-plaintext highlighter-rouge">deref</code> 隐式强制转换，具体后面会学到</li>
    </ul>
  </li>
</ul>

<h2 id="字符串索引">字符串索引</h2>

<blockquote>
  <p>在其它语言中，使用索引的方式访问字符串的某个字符或者子串是很正常的行为 <strong>(s[0])</strong>，但是在 Rust 中就会报错：</p>
</blockquote>

<h3 id="深入字符串内部">深入字符串内部</h3>

<ul>
  <li>字符串的底层的数据存储格式实际上是 [u8]，一个字节数组。
    <ul>
      <li>对于 <code class="language-plaintext highlighter-rouge">let hello = String::from("Hola");</code>这行代码来说，Hola 的长度是 4 个字节，因为 “Hold” 中的每个字母在 UTF-8 编码中仅占用 1 个字节</li>
      <li>但对于 <code class="language-plaintext highlighter-rouge">let hello = String::from("中国人")；</code>这个来说，看起来字符串的长度是 3，但实际上是 9，因为汉字在 UTF-8 中的长度是 3 个字节。</li>
    </ul>
  </li>
</ul>

<h3 id="字符串的不同表现形式没太看懂">字符串的不同表现形式（没太看懂</h3>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">现在看一下用梵文写的字符串</span> <span class="err">“</span><span class="n">नमस्ते</span><span class="err">”</span><span class="p">,</span> <span class="n">它底层的字节数组如下形式</span><span class="err">：</span>

<span class="p">[</span><span class="mi">224</span><span class="p">,</span> <span class="mi">164</span><span class="p">,</span> <span class="mi">168</span><span class="p">,</span> <span class="mi">224</span><span class="p">,</span> <span class="mi">164</span><span class="p">,</span> <span class="mi">174</span><span class="p">,</span> <span class="mi">224</span><span class="p">,</span> <span class="mi">164</span><span class="p">,</span> <span class="mi">184</span><span class="p">,</span> <span class="mi">224</span><span class="p">,</span> <span class="mi">165</span><span class="p">,</span> <span class="mi">141</span><span class="p">,</span> <span class="mi">224</span><span class="p">,</span> <span class="mi">164</span><span class="p">,</span> <span class="mi">164</span><span class="p">,</span>
<span class="mi">224</span><span class="p">,</span> <span class="mi">165</span><span class="p">,</span> <span class="mi">135</span><span class="p">]</span>
<span class="n">长度是</span> <span class="mi">18</span> <span class="n">个字节</span><span class="err">，</span><span class="n">这也是计算机最终存储该字符串的形式</span><span class="err">。</span><span class="n">如果从字符的形式去看</span><span class="err">，</span><span class="n">则是</span><span class="err">：</span>

<span class="p">[</span><span class="sc">'न'</span><span class="p">,</span> <span class="sc">'म'</span><span class="p">,</span> <span class="sc">'स'</span><span class="p">,</span> <span class="sc">'्'</span><span class="p">,</span> <span class="sc">'त'</span><span class="p">,</span> <span class="sc">'े'</span><span class="p">]</span>
<span class="n">但是这种形式下</span><span class="err">，</span><span class="n">第四和六两个字母根本就不存在</span><span class="err">，</span><span class="n">没有任何意义</span><span class="err">，</span><span class="n">接着再从字母串的形式去看</span><span class="err">：</span>

<span class="p">[</span><span class="s">"न"</span><span class="p">,</span> <span class="s">"म"</span><span class="p">,</span> <span class="s">"स्"</span><span class="p">,</span> <span class="s">"ते"</span><span class="p">]</span>
</code></pre></div></div>

<ul>
  <li>Rust 提供了不同的字符串的展现方式</li>
  <li>还有一个原因导致了 Rust 不允许去索引字符串：因为索引操作，我们总是期望它的性能表现是 O(1)，然而对于 <code class="language-plaintext highlighter-rouge">String</code> 类型来说，无法保证这一点，因为 Rust 可能需要从 0 开始去遍历字符串来定位合法的字符</li>
</ul>

<h2 id="字符串切片">字符串切片</h2>

<ul>
  <li>在通过索引区间来访问字符串时，<strong>需要格外的小心</strong>，一不注意，就会导致你程序的崩溃！</li>
</ul>

<h2 id="操作字符串">操作字符串</h2>

<p>由于 String 是可变字符串，Rust 中提供了字符串的修改，添加，删除等常用方法：</p>

<h3 id="追加push">追加（push）</h3>

<ul>
  <li>push() 方法追加字符到尾部</li>
  <li>push_str() 追加字符串字面量</li>
  <li><strong>都是在原有的字符串上追加，对原数据进行修改，不会返回新的数据</strong>
    <ul>
      <li>因此，该字符串必须是可变的，即字符串变量必须由 mut 关键字修饰</li>
    </ul>
  </li>
</ul>

<h3 id="插入insert">插入（insert）</h3>

<ul>
  <li>insert() 插入单个字符</li>
  <li>insert_str() 插入字符串字面量</li>
  <li>这两个方法需要传入两个参数，first 插入位置的索引，second 要插入的字符（串）
    <ul>
      <li>索引从 0 开始，如果越界，则会发生错误</li>
    </ul>
  </li>
  <li><strong>都是在原有的字符串上追加，对原数据进行修改，不会返回新的数据</strong>
    <ul>
      <li>因此，该字符串必须是可变的，即字符串变量必须由 mut 关键字修饰</li>
    </ul>
  </li>
</ul>

<h3 id="替换replace">替换（replace）</h3>

<blockquote>
  <p>如果想要把字符串中的某个字符串替换成其它的字符串，那可以使用 <code class="language-plaintext highlighter-rouge">replace()</code> 方法。与替换有关的方法有三个。</p>
</blockquote>

<ul>
  <li>replace 适用于 String 类型 和 &amp;str 类型
    <ul>
      <li>两个参数，first 要被替换的字符串，second 新的字符串</li>
      <li>该方法会替换所有匹配到的字符串</li>
      <li><strong>该方法是返回一个新的字符串，而不是原本的字符串</strong></li>
    </ul>
  </li>
  <li>replacen 适用于 String 类型 和 &amp;str 类型
    <ul>
      <li>三个参数，first 要被替换的字符串，second 新的字符串，third 表示替换的个数（也就是可以选择不全部替换）</li>
      <li><strong>该方法是返回一个新的字符串，而不是原本的字符串</strong></li>
    </ul>
  </li>
  <li>replace_range 该方法仅适用于 <code class="language-plaintext highlighter-rouge">String</code> 类型
    <ul>
      <li>两个参数，first 要替换字符串的<strong>范围</strong>，second 新的字符串</li>
      <li><strong>该方法是直接操作原来的字符串，不会返回新的字符串。</strong>
        <ul>
          <li><strong>该方法需要使用 <code class="language-plaintext highlighter-rouge">mut</code> 关键字修饰</strong>。</li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

<h3 id="删除delete">删除（delete）</h3>

<blockquote>
  <p>与字符串删除相关的方法有 4 个，他们分别是 <code class="language-plaintext highlighter-rouge">pop()</code>，<code class="language-plaintext highlighter-rouge">remove()</code>，<code class="language-plaintext highlighter-rouge">truncate()</code>，<code class="language-plaintext highlighter-rouge">clear()</code>。这四个方法仅适用于 <code class="language-plaintext highlighter-rouge">String</code> 类型</p>
</blockquote>

<p><strong>字符串删除操作，均是对原数据的修改，所以必须是可变引用，即需要 mut 关键字声明</strong></p>

<ul>
  <li>pop() 删除并返回最后一个字符
    <ul>
      <li>没有参数，注意字符越界问题</li>
    </ul>
  </li>
  <li>remove() 删除并返回字符串中指定位置的字符
    <ul>
      <li>有一个参数，表示删除的字符的起始索引位置，注意字符越界问题</li>
    </ul>
  </li>
  <li>truncate() 删除字符串中从指定位置开始到结尾的全部字符
    <ul>
      <li>没有返回值</li>
      <li>注意字符越界问题</li>
    </ul>
  </li>
  <li>clear() 清空字符串
    <ul>
      <li>没有返回值</li>
      <li>相当于 truncate(0)</li>
    </ul>
  </li>
</ul>

<h3 id="连接concatenate">连接（concatenate）</h3>

<ul>
  <li>
    <p><code class="language-plaintext highlighter-rouge">+</code> 或者 <code class="language-plaintext highlighter-rouge">+=</code> 连接字符串</p>

    <ul>
      <li>
        <p>需要右边的参数必须是<strong>字符串的切片引用类型（&amp;str）</strong></p>
      </li>
      <li>
        <p>其实相当于调用 <code class="language-plaintext highlighter-rouge">std::string.add()</code></p>

        <ul>
          <li>
            <div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="k">fn</span> <span class="nf">add</span><span class="p">(</span><span class="k">self</span><span class="p">,</span> <span class="n">s</span><span class="p">:</span> <span class="o">&amp;</span><span class="nb">str</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">String</span>
</code></pre></div>            </div>
          </li>
        </ul>
      </li>
      <li>
        <p><code class="language-plaintext highlighter-rouge">+</code>是返回一个新的字符串，所以变量可以不需要 mut 关键词修饰</p>
      </li>
      <li>
        <p><code class="language-plaintext highlighter-rouge">+</code>左边是一个 String 类型，在执行过连接之后，就会涉及到所有权转移</p>

        <ul>
          <li><code class="language-plaintext highlighter-rouge">let s3 = s1 + &amp;s2</code> s1 就被释放了</li>
        </ul>
      </li>
    </ul>
  </li>
  <li>
    <p>使用 format！连接字符串</p>

    <ul>
      <li>
        <p><code class="language-plaintext highlighter-rouge">format!</code> 这种方式适用于 <code class="language-plaintext highlighter-rouge">String</code> 和 <code class="language-plaintext highlighter-rouge">&amp;str</code> 。<code class="language-plaintext highlighter-rouge">format!</code> 的用法与 <code class="language-plaintext highlighter-rouge">print!</code> 的用法类似</p>

        <div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">let</span> <span class="n">s1</span> <span class="o">=</span> <span class="s">"hello"</span><span class="p">;</span>
    <span class="k">let</span> <span class="n">s2</span> <span class="o">=</span> <span class="nn">String</span><span class="p">::</span><span class="nf">from</span><span class="p">(</span><span class="s">"rust"</span><span class="p">);</span>
    <span class="k">let</span> <span class="n">s</span> <span class="o">=</span> <span class="nd">format!</span><span class="p">(</span><span class="s">"{} {}!"</span><span class="p">,</span> <span class="n">s1</span><span class="p">,</span> <span class="n">s2</span><span class="p">);</span>
    <span class="nd">println!</span><span class="p">(</span><span class="s">"{}"</span><span class="p">,</span> <span class="n">s</span><span class="p">);</span>
<span class="p">}</span>
<span class="c1">// hello rust!</span>
</code></pre></div>        </div>
      </li>
    </ul>
  </li>
</ul>

<h2 id="字符串转义">字符串转义</h2>

<ul>
  <li>
    <p>我们可以通过转义的方式 <code class="language-plaintext highlighter-rouge">\</code> 输出 ASCII 和 Unicode 字符</p>

    <div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="c1">// 通过 \ + 字符的十六进制表示，转义输出一个字符</span>
    <span class="k">let</span> <span class="n">byte_escape</span> <span class="o">=</span> <span class="s">"I'm writing </span><span class="se">\x52\x75\x73\x74</span><span class="s">!"</span><span class="p">;</span>
    <span class="nd">println!</span><span class="p">(</span><span class="s">"What are you doing</span><span class="se">\x3F</span><span class="s"> (</span><span class="se">\\</span><span class="s">x3F means ?) {}"</span><span class="p">,</span> <span class="n">byte_escape</span><span class="p">);</span>

    <span class="c1">// \u 可以输出一个 unicode 字符</span>
    <span class="k">let</span> <span class="n">unicode_codepoint</span> <span class="o">=</span> <span class="s">"</span><span class="se">\u{211D}</span><span class="s">"</span><span class="p">;</span>
    <span class="k">let</span> <span class="n">character_name</span> <span class="o">=</span> <span class="s">"</span><span class="se">\"</span><span class="s">DOUBLE-STRUCK CAPITAL R</span><span class="se">\"</span><span class="s">"</span><span class="p">;</span>

    <span class="nd">println!</span><span class="p">(</span>
        <span class="s">"Unicode character {} (U+211D) is called {}"</span><span class="p">,</span>
        <span class="n">unicode_codepoint</span><span class="p">,</span> <span class="n">character_name</span>
    <span class="p">);</span>

    <span class="c1">// 换行了也会保持之前的字符串格式</span>
    <span class="c1">// 使用\忽略换行符</span>
    <span class="k">let</span> <span class="n">long_string</span> <span class="o">=</span> <span class="s">"String literals
                        can span multiple lines.
                        The linebreak and indentation here -&gt;</span><span class="err">\</span><span class="s">
                        &lt;- can be escaped too!"</span><span class="p">;</span>
    <span class="nd">println!</span><span class="p">(</span><span class="s">"{}"</span><span class="p">,</span> <span class="n">long_string</span><span class="p">);</span>
<span class="p">}</span>
<span class="c1">// 程序执行后输出结果：</span>
<span class="c1">// What are you doing? (\x3F means ?) I'm writing Rust!</span>
<span class="c1">// Unicode character ℝ (U+211D) is called "DOUBLE-STRUCK CAPITAL R"</span>
<span class="c1">// String literals</span>
<span class="c1">//                        can span multiple lines.</span>
<span class="c1">//                        The linebreak and indentation here -&gt;&lt;- can be escaped too!</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>可以通过 <code class="language-plaintext highlighter-rouge">\</code> <code class="language-plaintext highlighter-rouge">##</code>保持字符串的原样</p>

    <div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="nd">println!</span><span class="p">(</span><span class="s">"{}"</span><span class="p">,</span> <span class="s">"hello </span><span class="se">\\</span><span class="s">x52</span><span class="se">\\</span><span class="s">x75</span><span class="se">\\</span><span class="s">x73</span><span class="se">\\</span><span class="s">x74"</span><span class="p">);</span>
    <span class="k">let</span> <span class="n">raw_str</span> <span class="o">=</span> <span class="s">r"Escapes don't work here: \x3F \u{211D}"</span><span class="p">;</span>
    <span class="nd">println!</span><span class="p">(</span><span class="s">"{}"</span><span class="p">,</span> <span class="n">raw_str</span><span class="p">);</span>

    <span class="c1">// 如果字符串包含双引号，可以在开头和结尾加 #</span>
    <span class="k">let</span> <span class="n">quotes</span> <span class="o">=</span> <span class="s">r#"And then I said: "There is no escape!""#</span><span class="p">;</span>
    <span class="nd">println!</span><span class="p">(</span><span class="s">"{}"</span><span class="p">,</span> <span class="n">quotes</span><span class="p">);</span>

    <span class="c1">// 如果还是有歧义，可以继续增加，没有限制</span>
    <span class="k">let</span> <span class="n">longer_delimiter</span> <span class="o">=</span> <span class="s">r###"A string with "# in it. And even "##!"###</span><span class="p">;</span>
    <span class="nd">println!</span><span class="p">(</span><span class="s">"{}"</span><span class="p">,</span> <span class="n">longer_delimiter</span><span class="p">);</span>
<span class="p">}</span>
<span class="c1">// 程序执行后输出结果：</span>
<span class="c1">// hello \x52\x75\x73\x74</span>
<span class="c1">// Escapes don't work here: \x3F \u{211D}</span>
<span class="c1">// And then I said: "There is no escape!"</span>
<span class="c1">// A string with "# in it. And even "##!</span>
</code></pre></div>    </div>
  </li>
</ul>

<h2 id="操作-utf-8-字符串">操作 UTF-8 字符串</h2>

<h3 id="字符">字符</h3>

<ul>
  <li>for in “中国人”.chars()
    <ul>
      <li>中</li>
      <li>国</li>
      <li>人</li>
    </ul>
  </li>
</ul>

<h3 id="字节">字节</h3>

<ul>
  <li>for in ““.bytes()
    <ul>
      <li>228</li>
      <li>184</li>
      <li>…</li>
    </ul>
  </li>
</ul>

<h3 id="获取子串">获取子串</h3>

<p>想要准确的从 UTF-8 字符串中获取子串是较为复杂的事情，例如想要从 <code class="language-plaintext highlighter-rouge">holla中国人नमस्ते</code> 这种变长的字符串中取出某一个子串，使用标准库你是做不到的。 你需要在 <code class="language-plaintext highlighter-rouge">crates.io</code> 上搜索 <code class="language-plaintext highlighter-rouge">utf8</code> 来寻找想要的功能。</p>

<p>可以考虑尝试下这个库：<a href="https://crates.io/crates/utf8_slice">utf8_slice</a>。</p>

<h2 id="字符串深度解析">字符串深度解析</h2>

<ul>
  <li>
    <p>为什么 String 可变，而字符串字面值 str 却不可变？</p>

    <ul>
      <li>对于 str，在编译的时候就知道其内容，最终文本可被直接硬编码进可执行文件中，这样使得字符串字面值快速且高效，这种实现主要得益于字符串字面值的不可变性。</li>
      <li>但有的文本，是在执行时动态生成的，那这种不能在编译时，将大小未知的的文本都放入内存中。</li>
    </ul>
  </li>
  <li>
    <p>对于 String 类型，为了支持 可变、可增长的文本片段，需要在堆上分配一块在编译时未知大小的内存用来存放内容，这些都是在程序运行时完成的</p>

    <ol>
      <li>首先向操作系统请求内存存放 String 对象</li>
      <li>在使用完成后，将内存释放，归还操作系统</li>
    </ol>

    <ul>
      <li>第二点就是 GC 环节</li>
    </ul>
  </li>
</ul>

<blockquote>
  <p>重点来了，到了第二部分，就是百家齐放的环节，在有<strong>垃圾回收 GC</strong> 的语言中，GC 来负责标记并清除这些不再使用的内存对象，这个过程都是自动完成，无需开发者关心，非常简单好用；但是在无 GC 的语言中，需要开发者手动去释放这些内存对象，就像创建对象需要通过编写代码来完成一样，未能正确释放对象造成的后果简直不可估量。</p>

  <p>对于 Rust 而言，安全和性能是写到骨子里的核心特性，如果使用 GC，那么会牺牲性能；如果使用手动管理内存，那么会牺牲安全，这该怎么办？为此，Rust 的开发者想出了一个无比惊艳的办法：变量在离开作用域后，就自动释放其占用的内存：</p>

  <p>与其它系统编程语言的 <code class="language-plaintext highlighter-rouge">free</code> 函数相同，Rust 也提供了一个释放内存的函数： <code class="language-plaintext highlighter-rouge">drop</code>，但是不同的是，其它语言要手动调用 <code class="language-plaintext highlighter-rouge">free</code> 来释放每一个变量占用的内存，而 Rust 则在变量离开作用域时，自动调用 <code class="language-plaintext highlighter-rouge">drop</code> 函数: 上面代码中，Rust 在结尾的 <code class="language-plaintext highlighter-rouge">}</code> 处自动调用 <code class="language-plaintext highlighter-rouge">drop</code>。</p>
</blockquote>]]></content><author><name>Bran.nie</name></author><category term="Rust" /><summary type="html"><![CDATA[Rust 基础入门 - 03]]></summary></entry><entry><title type="html">Rust 基础入门 - 02（所有权和借用）</title><link href="https://blog.bran-nie.cn/2023/10/16/rust-learn-base-02/" rel="alternate" type="text/html" title="Rust 基础入门 - 02（所有权和借用）" /><published>2023-10-16T00:00:00+08:00</published><updated>2023-10-16T00:00:00+08:00</updated><id>https://blog.bran-nie.cn/2023/10/16/rust-learn-base-02</id><content type="html" xml:base="https://blog.bran-nie.cn/2023/10/16/rust-learn-base-02/"><![CDATA[<p>这是 bran 学习 Rust 的笔记 &amp; 心得系列，目前是 Rust 基础入门 - 02（所有权和借用），刚开始学哦～ 加油！！！</p>

<h2 id="所有权">所有权</h2>

<p>因为程序要运行，就要和计算机内存打交道，那么如何在内存中申请空间、释放空间，成了语言设计的重中之重。</p>

<p>应用程序和计算机内存的爱恨情仇，目前有三种流派。</p>

<ol>
  <li><strong>垃圾回收机制（Garbage Collection）</strong>，在程序运行时，不断寻找不再使用的内存，进行回收处理。典型代表：Java、Go、JavaScript</li>
  <li><strong>手动管理内存的分配和释放</strong>，在程序中，通过函数调用的方式来申请和释放内存。典型代表：C、C++、早期的 OC</li>
  <li><strong>通过所有权来管理内存</strong>，编译器在编译时会根据一系列的规则进行检查</li>
</ol>

<p>Rust 选择了第三种，这种检查只发生在编译期，因此对于程序运行期，不会有任何性能上的损失。（还不太懂这个为什么没有，带着疑惑继续学习）</p>

<h2 id="栈stack和堆heap">栈(Stack)和堆(Heap)</h2>

<p>在 Rust 中，栈和堆是最核心的数据结构，数据是位于栈上，还是堆上，非常重要，因为这会影响程序的行为和性能</p>

<blockquote>
  <p>Me：对于 jser 来说，这个概念应该好理解的</p>
</blockquote>

<h3 id="栈">栈</h3>

<ul>
  <li>按照顺序存储值并以相反的顺序取出值，（先进后出）（后进先出）。</li>
  <li>增加数据叫<strong><em>进栈</em></strong>，移出数据叫<strong><em>出栈</em></strong></li>
  <li>栈中的所有数据都必须占有<strong>已知且固定大小的内存空间</strong>，假设数据大小是未知的，那么取出数据时，将无法取到想要的数据。。</li>
</ul>

<h3 id="堆">堆</h3>

<ul>
  <li>与栈不同，大小未知或者可能变化的数据，可以存放在堆上</li>
  <li>堆的内存空间不连续</li>
  <li>向堆上存放数据时，先申请一定大小的空间，操作系统在堆的某处找到足够大的空间后，将其标记为已使用，并返回一个表示该地址的<strong>指针</strong>。该过程被称为<strong>在堆上分配内存</strong>，有时简称“分配”（allocating）</li>
  <li>该指针会被推入栈中，后续通过该指针来访问实际的内存位置，进而访问数据</li>
</ul>

<h3 id="性能区别">性能区别</h3>

<ul>
  <li>写入方面：入栈比在堆上分配内存更快</li>
  <li>读取方面：由于堆的内存地址也是在栈中，再加上现代的 CPU 性能及高速缓存，栈的访问速度也是优于堆的</li>
</ul>

<p>因此，处理器处理分配在栈上数据会比在堆上的数据更加高效。</p>

<h3 id="所有权与堆栈">所有权与堆栈</h3>

<p>对于其他很多编程语言，你确实无需理解堆栈的原理，但是<strong>在 Rust 中，明白堆栈的原理，对于我们理解所有权的工作原理会有很大的帮助</strong>。</p>

<h2 id="所有权原则">所有权原则</h2>

<blockquote>
  <ul>
    <li>Rust 中每一个值都被一个变量所拥有，该变量被称为值的所有者</li>
    <li>一个值同时只能被一个变量所拥有</li>
    <li>当所有者（变量）离开作用域范围内时，这个值将被丢弃（drop）</li>
  </ul>
</blockquote>

<h3 id="变量作用域">变量作用域</h3>

<ul>
  <li>
    <p>类似 js，函数，块级作用域</p>
  </li>
  <li>
    <p>Rust 中，字符是基本类型，是和 String 不一样的，js 中倒是没有这种区别。字符<code class="language-plaintext highlighter-rouge">''</code>，字符串<code class="language-plaintext highlighter-rouge">""</code></p>
  </li>
</ul>

<h3 id="简单介绍-string-类型">简单介绍 String 类型</h3>

<ul>
  <li>
    <p>字符串有<strong>字符串字面量</strong>，也有<strong>动态字符串类型</strong>。这和 js 很不一样，js 很灵活。</p>
  </li>
  <li>
    <p>因为 String 是存储在堆上，是动态的，所以可以修改</p>

    <div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">c</span> <span class="o">=</span> <span class="sc">'c'</span><span class="p">;</span> <span class="c1">// 字符</span>
<span class="k">let</span> <span class="n">s1</span> <span class="o">=</span> <span class="s">"hello"</span><span class="p">;</span> <span class="c1">// 字符串，字符串字面量，一经确定不可再变，已经被硬编码到程序代码中了。</span>
<span class="k">let</span> <span class="n">s2</span> <span class="o">=</span> <span class="nn">String</span><span class="p">::</span><span class="nf">from</span><span class="p">(</span><span class="s">"hello"</span><span class="p">);</span> <span class="c1">// 字符串，动态字符串类型</span>
<span class="n">s2</span><span class="nf">.push_str</span><span class="p">(</span><span class="s">", world!"</span><span class="p">);</span> <span class="c1">// 因为是动态的，所以可以再修改</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>上面代码中的 <code class="language-plaintext highlighter-rouge">String::from</code>中的：是调用操作符，类似 js 中的 <code class="language-plaintext highlighter-rouge">String.from</code> 的 .（PS：我觉得 . 多好用啊，hhh）</p>
  </li>
</ul>

<h2 id="变量绑定背后的数据交互">变量绑定背后的数据交互</h2>

<h3 id="转移所有权">转移所有权</h3>

<pre><code class="language-rus">let x = 5;
let y = x;
</code></pre>

<p>声明 x，赋值 5，声明 y，将 x 赋值给 y，x,y 都等于 5，因为整数是 Rust 基本数据类型，是固定大小的简单值，因此这两个值都是通过<strong>自动拷贝</strong>的方式来赋值的，都被存在栈中，完全无需在堆上分配内存。</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">s1</span> <span class="o">=</span> <span class="nn">String</span><span class="p">::</span><span class="nf">from</span><span class="p">(</span><span class="s">"hello"</span><span class="p">);</span>
<span class="k">let</span> <span class="n">s2</span> <span class="o">=</span> <span class="n">s1</span><span class="p">;</span>
</code></pre></div></div>

<p>当变量类型是 String 时，就不是<strong>自动拷贝</strong>了，因为 String 不是基本类型，而且是存储在堆上的数据，</p>

<p>实际上，String 是一个复杂类型，由<strong>存储在栈中的指针、字符串长度、字符串容量</strong>共同组成，其中堆指针式最重要的（因为它指向数据在内存中真正存放的地址，可以理解为门牌号 hhh）至于长度、容量，也就是附加信息，容量是分配大小，长度是已使用大小，（多大的房子和已经住了多少人，hhh）</p>

<p>因此，对 String 类型的 copy，分两种情况：</p>

<ol>
  <li>所有都拷贝，也就是深拷贝</li>
  <li>只拷贝指针、容量、大小这些 id 卡
    <ol>
      <li>但是这样就违背了一个值只能有一个所有者！</li>
      <li>假定只拷贝指针等，那么上面代码中 s1 和 s2 同时都是值的所有者，当 s1、s2 离开作用域时，Rust 都会尝试释放相同的内存，但第二次释放时，不就出现错误了吗，</li>
    </ol>
  </li>
</ol>

<p>因此，Rust 中，s1 赋值给 s2 时，s1 不再有效，因此也无需 drop s1，值当所有权就到 s2 了，s1 在赋值之后，就失效了。</p>

<p><img src="https://raw.githubusercontent.com/bran-nie/blog_images/images/blog/20231026150530.png" alt="" /></p>

<ul>
  <li>在 js 中，对 Object 的拷贝，有深拷贝（deep copy）和浅拷贝（shallow copy），只拷贝指针、（长度、容量）而不拷贝数据，听起来像是浅拷贝，但又因为 Rust 同时使第一个变量 s1 无效了，因此这个操作，被称为<strong>移动（move）</strong>，而不是浅拷贝。</li>
</ul>

<p><img src="https://raw.githubusercontent.com/bran-nie/blog_images/images/blog/rust-move.jpg" alt="s1 moved to s2" /></p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">let</span> <span class="n">x</span><span class="p">:</span> <span class="o">&amp;</span><span class="nb">str</span> <span class="o">=</span> <span class="s">"hello, world"</span><span class="p">;</span>
    <span class="k">let</span> <span class="n">y</span> <span class="o">=</span> <span class="n">x</span><span class="p">;</span>
    <span class="nd">println!</span><span class="p">(</span><span class="s">"{},{}"</span><span class="p">,</span><span class="n">x</span><span class="p">,</span><span class="n">y</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<ul>
  <li>
    <p>但上面这段代码，就不会报错，文章说：</p>
  </li>
  <li>
    <blockquote>
      <p>这段代码和之前的 <code class="language-plaintext highlighter-rouge">String</code> 有一个本质上的区别：在 <code class="language-plaintext highlighter-rouge">String</code> 的例子中 <code class="language-plaintext highlighter-rouge">s1</code> 持有了通过<code class="language-plaintext highlighter-rouge">String::from("hello")</code> 创建的值的所有权，而这个例子中，<code class="language-plaintext highlighter-rouge">x</code> 只是引用了存储在二进制中的字符串 <code class="language-plaintext highlighter-rouge">"hello, world"</code>，并没有持有所有权。</p>

      <p>因此 <code class="language-plaintext highlighter-rouge">let y = x</code> 中，仅仅是对该引用进行了拷贝，此时 <code class="language-plaintext highlighter-rouge">y</code> 和 <code class="language-plaintext highlighter-rouge">x</code> 都引用了同一个字符串。</p>
    </blockquote>
  </li>
  <li>
    <p>我还不是很理解，按照上面提到的所有权原则，每个值都有一个所有者，一个值只能同时有一个所有者，那么，这句话<strong>此时 <code class="language-plaintext highlighter-rouge">y</code> 和 <code class="language-plaintext highlighter-rouge">x</code> 都引用了同一个字符串</strong>又是什么情况呢，继续看。</p>

    <ul>
      <li>笔记 - <code class="language-plaintext highlighter-rouge">&amp;T</code> 表示是 不可变引用，T 为类型</li>
      <li>笔记- <code class="language-plaintext highlighter-rouge">&amp;mut</code> 表示是 可变引用</li>
    </ul>
  </li>
</ul>

<h3 id="克隆深拷贝">克隆（深拷贝）</h3>

<ul>
  <li>如果代码性能无关紧要，例如初始化程序时，或者在某段时间只会执行一次时，你可以使用 <code class="language-plaintext highlighter-rouge">clone</code> 来简化编程。但是对于执行较为频繁的代码(热点路径)，使用 <code class="language-plaintext highlighter-rouge">clone</code> 会极大的降低程序性能，需要小心使用</li>
</ul>

<h3 id="拷贝浅拷贝">拷贝（浅拷贝）</h3>

<ul>
  <li>浅拷贝只发生在栈上，因此性能很高，在日常编程中，浅拷贝无处不在</li>
  <li>Rust 有一个叫做 Copy 的特征
    <ul>
      <li>如果一个类型拥有 Copy 特征，一个旧的变量在被赋值给其他变量后，仍然可用</li>
      <li>任何基本类型的组合可以 copy，不需要分配内存或者某种形式资源的类型可以 copy。（具体可以查看给定类型的文档来确认
        <ul>
          <li>所有整数类型</li>
          <li>布尔类型</li>
          <li>浮点数类型</li>
          <li>字符类型 char</li>
          <li>元组，且其包含的也是可以 copy 的类型时</li>
          <li>不可变引用 &amp;T</li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

<h2 id="函数传值与返回">函数传值与返回</h2>

<ul>
  <li>
    <p>将值传递给函数，一样会发生<code class="language-plaintext highlighter-rouge">移动</code>或者<code class="language-plaintext highlighter-rouge">复制</code></p>
  </li>
  <li>
    <div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
      <span class="k">let</span> <span class="n">s</span> <span class="o">=</span> <span class="nn">String</span><span class="p">::</span><span class="nf">from</span><span class="p">(</span><span class="s">"hello"</span><span class="p">);</span>  <span class="c1">// s 进入作用域</span>

      <span class="nf">takes_ownership</span><span class="p">(</span><span class="n">s</span><span class="p">);</span>             <span class="c1">// s 的值移动到函数里 ...</span>
                                      <span class="c1">// ... 所以到这里不再有效</span>

      <span class="k">let</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">5</span><span class="p">;</span>                      <span class="c1">// x 进入作用域</span>

      <span class="nf">makes_copy</span><span class="p">(</span><span class="n">x</span><span class="p">);</span>                  <span class="c1">// x 应该移动函数里，</span>
                                      <span class="c1">// 但 i32 是 Copy 的，所以在后面可继续使用 x</span>

  <span class="p">}</span> <span class="c1">// 这里, x 先移出了作用域，然后是 s。但因为 s 的值已被移走，</span>
    <span class="c1">// 所以不会有特殊操作</span>

  <span class="k">fn</span> <span class="nf">takes_ownership</span><span class="p">(</span><span class="n">some_string</span><span class="p">:</span> <span class="nb">String</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// some_string 进入作用域</span>
      <span class="nd">println!</span><span class="p">(</span><span class="s">"{}"</span><span class="p">,</span> <span class="n">some_string</span><span class="p">);</span>
  <span class="p">}</span> <span class="c1">// 这里，some_string 移出作用域并调用 `drop` 方法。占用的内存被释放</span>

  <span class="k">fn</span> <span class="nf">makes_copy</span><span class="p">(</span><span class="n">some_integer</span><span class="p">:</span> <span class="nb">i32</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// some_integer 进入作用域</span>
      <span class="nd">println!</span><span class="p">(</span><span class="s">"{}"</span><span class="p">,</span> <span class="n">some_integer</span><span class="p">);</span>
  <span class="p">}</span> <span class="c1">// 这里，some_integer 移出作用域。不会有特殊操作</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>相比 js 的灵活，在 rust 中，就不能任意了</p>
  </li>
  <li>Rust 麻烦：<strong>总是把一个值传来传去来使用它</strong>。 传入一个函数，很可能还要从该函数传出去，结果就是语言表达变得非常啰嗦，幸运的是，Rust 提供了新功能解决这个问题。</li>
</ul>

<h2 id="引用与借用">引用与借用</h2>

<ul>
  <li>
    <p>买房和租房？（不是特别合适</p>

    <div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">let</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">5</span><span class="p">;</span> <span class="c1">// 值 5 的所有者是 变量 x</span>
    <span class="k">let</span> <span class="n">y</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">x</span><span class="p">;</span> <span class="c1">// 所有者 x 将值 5 出租给 y，&amp;x 表示签了出租合同😂</span>

    <span class="nd">assert_eq!</span><span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="n">x</span><span class="p">);</span> <span class="c1">// 5 是 x 的，没问题</span>
    <span class="nd">assert_eq!</span><span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="o">*</span><span class="n">y</span><span class="p">);</span> <span class="c1">// 5 里面住的是 y，是租用，所以是 *y，* 表明解引用，找到 y 的房东</span>
    <span class="c1">// 但要是 assert_eq!(5, y)，就不对了，只是出租，不是卖给 y。</span>
<span class="p">}</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>对比，不允许比较不同的类型，必须使用解引用运算符，解出引用所指向的值</p>
  </li>
</ul>

<h2 id="不可变引用-t">不可变引用 &amp;T</h2>

<ul>
  <li>
    <p>将变量的引用作为参数传递</p>
  </li>
  <li>
    <div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
      <span class="k">let</span> <span class="n">s1</span> <span class="o">=</span> <span class="nn">String</span><span class="p">::</span><span class="nf">from</span><span class="p">(</span><span class="s">"hello"</span><span class="p">);</span>

      <span class="c1">// 传递给函数的是 s1 的引用</span>
      <span class="k">let</span> <span class="n">len</span> <span class="o">=</span> <span class="nf">calculate_length</span><span class="p">(</span><span class="o">&amp;</span><span class="n">s1</span><span class="p">);</span>

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

  <span class="k">fn</span> <span class="nf">calculate_length</span><span class="p">(</span><span class="n">s</span><span class="p">:</span> <span class="o">&amp;</span><span class="nb">String</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">usize</span> <span class="p">{</span>
      <span class="n">s</span><span class="nf">.len</span><span class="p">()</span>
  <span class="p">}</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>无需像前面的代码一样：先通过函数参数传入变量（所有权），然后再通过函数返回来传出所有权，代码更加简洁</p>
  </li>
  <li>
    <p>函数<code class="language-plaintext highlighter-rouge">calculate_length</code>的参数类型从 String 变为 &amp;String</p>
  </li>
  <li>&amp; 符号即是引用，它允许我们使用变量的值，但是不获取所有权。</li>
</ul>

<h2 id="可变引用-mut-t">可变引用 &amp;mut T</h2>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">let</span> <span class="k">mut</span> <span class="n">s</span> <span class="o">=</span> <span class="nn">String</span><span class="p">::</span><span class="nf">from</span><span class="p">(</span><span class="s">"hello"</span><span class="p">);</span>

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

<span class="k">fn</span> <span class="nf">change</span><span class="p">(</span><span class="n">some_string</span><span class="p">:</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="nb">String</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">some_string</span><span class="nf">.push_str</span><span class="p">(</span><span class="s">", world"</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<ul>
  <li>修改传值和参数类型就可以在函数中也能修改变量了</li>
</ul>

<h3 id="可变引用同时只能存在一个">可变引用同时只能存在一个</h3>

<ul>
  <li>这样限制的好处就是使 Rust 在编译期就避免数据竞争，数据竞争可由以下行为造成：
    <ul>
      <li>两个或更多的指针同时访问同一数据</li>
      <li>至少有一个指针被用来写入数据</li>
      <li>没有同步数据访问的机制</li>
    </ul>
  </li>
  <li>
    <p>数据竞争大白话，俺的理解就是，同一份数据多个变量访问，有的写入有的读取，导致数据出现问题啦</p>
  </li>
  <li>通过大括号来手动限制变量的作用域</li>
</ul>

<h3 id="可变引用和不可变引用不能同时存在">可变引用和不可变引用不能同时存在</h3>

<p>Rust 的编译器一直在优化，早期的时候，引用的作用域跟变量作用域是一致的，这对日常使用带来了很大的困扰，你必须非常小心的去安排可变、不可变变量的借用，免得无法通过编译，例如以下代码：</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
   <span class="k">let</span> <span class="k">mut</span> <span class="n">s</span> <span class="o">=</span> <span class="nn">String</span><span class="p">::</span><span class="nf">from</span><span class="p">(</span><span class="s">"hello"</span><span class="p">);</span>

    <span class="k">let</span> <span class="n">r1</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">s</span><span class="p">;</span>
    <span class="k">let</span> <span class="n">r2</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">s</span><span class="p">;</span>
    <span class="nd">println!</span><span class="p">(</span><span class="s">"{} and {}"</span><span class="p">,</span> <span class="n">r1</span><span class="p">,</span> <span class="n">r2</span><span class="p">);</span>
    <span class="c1">// 新编译器中，r1,r2作用域在这里结束</span>

    <span class="k">let</span> <span class="n">r3</span> <span class="o">=</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="n">s</span><span class="p">;</span>
    <span class="nd">println!</span><span class="p">(</span><span class="s">"{}"</span><span class="p">,</span> <span class="n">r3</span><span class="p">);</span>
<span class="p">}</span> <span class="c1">// 老编译器中，r1、r2、r3作用域在这里结束</span>
  <span class="c1">// 新编译器中，r3作用域在这里结束</span>
</code></pre></div></div>

<ul>
  <li>新的编译器（Rust 1.31 开始）<strong>引用作用域的结束位置从花括号变成最后一次使用的位置</strong></li>
  <li>NLL（Non-Lexical Lifetimes）专门用于找到某个引用在作用域(})结束前就不再被使用的位置</li>
</ul>

<h2 id="垂悬引用dangling-references">垂悬引用(Dangling References)</h2>

<ul>
  <li>
    <p>是返回了一个借用的值，但这个值已经被 drop 了</p>

    <div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fn</span> <span class="nf">dangle</span><span class="p">()</span> <span class="k">-&gt;</span> <span class="o">&amp;</span><span class="nb">String</span> <span class="p">{</span> <span class="c1">// dangle 返回一个字符串的引用</span>

    <span class="k">let</span> <span class="n">s</span> <span class="o">=</span> <span class="nn">String</span><span class="p">::</span><span class="nf">from</span><span class="p">(</span><span class="s">"hello"</span><span class="p">);</span> <span class="c1">// s 是一个新字符串</span>

    <span class="o">&amp;</span><span class="n">s</span> <span class="c1">// 返回字符串 s 的引用</span>
<span class="p">}</span> <span class="c1">// 这里 s 离开作用域并被丢弃。其内存被释放。</span>
  <span class="c1">// 危险！</span>

</code></pre></div>    </div>
  </li>
  <li>
    <p>解决方案：直接返回 String 就可以了，这样就是所有权转移给外面的调用者了。</p>
  </li>
  <li>
    <div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="k">fn</span> <span class="nf">no_dangle</span><span class="p">()</span> <span class="k">-&gt;</span> <span class="nb">String</span> <span class="p">{</span>
      <span class="k">let</span> <span class="n">s</span> <span class="o">=</span> <span class="nn">String</span><span class="p">::</span><span class="nf">from</span><span class="p">(</span><span class="s">"hello"</span><span class="p">);</span>

      <span class="n">s</span>
  <span class="p">}</span>

</code></pre></div>    </div>
  </li>
</ul>]]></content><author><name>Bran.nie</name></author><category term="Rust" /><summary type="html"><![CDATA[Rust 基础入门 - 02]]></summary></entry><entry><title type="html">初识 Rust，未来 X 年最值得学习的语言</title><link href="https://blog.bran-nie.cn/2023/10/12/rust-hello-world/" rel="alternate" type="text/html" title="初识 Rust，未来 X 年最值得学习的语言" /><published>2023-10-12T00:00:00+08:00</published><updated>2023-10-12T00:00:00+08:00</updated><id>https://blog.bran-nie.cn/2023/10/12/rust-hello-world</id><content type="html" xml:base="https://blog.bran-nie.cn/2023/10/12/rust-hello-world/"><![CDATA[<p>对于新手，我发现一个超级风趣，超级丰富，强烈推荐的中文学习手册（圣经）<a href="https://course.rs/about-book.html">Rust 语言圣经</a>。目前我刚刚开始看，我也会记录一下我的 rust 学习之路，hope and hold on</p>

<h2 id="关于学习">关于学习</h2>

<p>打算多投入，边看文章，边敲代码，边写记录，😁</p>

<h2 id="记录">记录</h2>

<ul>
  <li>风格真的很风趣，我想应该降低不少学习的心理难度！</li>
  <li>作者给大家的三点建议</li>
</ul>

<blockquote>
  <p>在学习 Go、Python 等编程语言时，你可能会一边工作、一边轻松愉快的学习它们，但是 Rust 不行。原因如文章开头所说，在学习 Rust 的同时你会收获很多语言之外的知识，因此 Rust 在入门阶段比很多编程语言要更难，但是一旦入门，你将收获一个全新的自己，成为一个更加优秀的程序员。</p>

  <p>在学习过程中，一开始可能会轻松愉快，但是在开始接触 Rust 核心概念时(所有权、借用、生命周期、智能指针等)，难度会陡然提升，此时就需要认真对待起来，否则会为后面埋下难以填补的坑: 结果最后你可能只有两个选择 - 重新学 or 放弃。</p>

  <ul>
    <li>要提前做好会遇到困难的准备，因为如上所说，学习 Rust 不仅仅是在学习一门编程语言</li>
    <li>不要抱着试一试的心态去试一试，否则是浪费时间和消耗学习激情，作为连续七年荣获全世界最受喜欢桂冠的语言，Rust 不仅仅是值得试一试 :)</li>
    <li>深入学习一本好书或教程</li>
  </ul>
</blockquote>]]></content><author><name>Bran.nie</name></author><category term="Blog" /><category term="Rust" /><summary type="html"><![CDATA[初识 Rust，未来 X 年最值得学习的语言]]></summary></entry><entry><title type="html">Rust 基础入门 - 01（变量、基本类型）</title><link href="https://blog.bran-nie.cn/2023/10/12/rust-learn-base-01/" rel="alternate" type="text/html" title="Rust 基础入门 - 01（变量、基本类型）" /><published>2023-10-12T00:00:00+08:00</published><updated>2023-10-12T00:00:00+08:00</updated><id>https://blog.bran-nie.cn/2023/10/12/rust-learn-base-01</id><content type="html" xml:base="https://blog.bran-nie.cn/2023/10/12/rust-learn-base-01/"><![CDATA[<p>这是 bran 学习 Rust 的笔记 &amp; 心得系列，目前是 Rust 基础入门 - 01（变量、基本类型），刚开始学哦～ 加油！！！</p>

<h2 id="变量绑定与解构">变量绑定与解构</h2>

<ul>
  <li>啊哈，变量和常量，let and const，jser 有没有很熟悉～</li>
  <li>println，梦回 c</li>
  <li>编译器太太太强了，错误提示，很清晰，个人粗略感受，就像是 ts 中的类型提示一样，但不仅限于类型！</li>
  <li>rust 变量屏蔽 - js 变量作用域，看起来像，但本质不太一样</li>
  <li>如果 jser 也用 typescript，那么，学习 rust 时，可以减少不少类型相关的基础</li>
  <li>因为我比较熟悉 js，因此在学习 rust 的时候，总是会对比两者</li>
</ul>

<h2 id="基本类型">基本类型</h2>

<ul>
  <li>rust 和 js 一样，数据类型有基本类型和（rust：单元类型）（js：引用类型）</li>
</ul>

<h3 id="数值类型">数值类型</h3>

<ul>
  <li>
    <p>对于数值类型，js 相比 rust，就太灵活了，rust 在声明数值的时候，默认是 i32，既然数值有严格的定义，那么可能会出现溢出的情况！因此，jser 在学习中，就要考虑这种情况，不能任意发挥哦</p>

    <ul>
      <li>isize、usize 是视架构而定的类型，取决于程序运行的计算机 CPU 类型：如果 CPU 是 32 位的，那这两个类型就是 32 位的，同理也可以是 64 位的；主要应用场景是用作集合的索引</li>
    </ul>
  </li>
  <li>
    <p>相比 js，多了<strong>浮点类型</strong>，rust 有两种类型：<code class="language-plaintext highlighter-rouge">f32</code>和<code class="language-plaintext highlighter-rouge">f64</code>，默认是 f64，浮点数根据<code class="language-plaintext highlighter-rouge">IEEE-754</code>标准实现的，f32 是单精度，f64 是双精度。现代 CPU 性能足够强大，f64 的速度几乎和 f32 相同，因此默认是 f64，因为精度更高</p>
  </li>
  <li>
    <p>在浮点数上，都会出现精度丢失问题，0.1 + 0.2 !== 0.3</p>
  </li>
  <li>
    <p>字符和数字值是 Rust 中仅有的可以用于判断是否为空的类型。</p>
  </li>
  <li>
    <p>Rust 的数值类型和运算，看似与其他语言较为相似，但是实际上，除了语法上的不同之处，还有存在一些差异点</p>

    <ul>
      <li>Rust 拥有相当多的数值类型，因此需要熟悉这些类型所<strong>占用的字节数</strong>，这样就知道<strong>该类型允许的大小范围</strong>，以及选择的类型是否能表达负数</li>
      <li><strong>类型转换必须是显式的</strong>，Rust 永远也不会偷偷把你的 16bit 整数转换成 32bit 的整数</li>
      <li>Rust 数值可以使用方法，例如可以用以下方法来将 <code class="language-plaintext highlighter-rouge">13.14</code>取整：<code class="language-plaintext highlighter-rouge">13.14_f32.round()</code>，这里使用了类型后缀，因为编译器需要知道 13.14 的具体类型</li>
    </ul>
  </li>
</ul>

<blockquote>
  <p>Rust 圣经作者：字符、布尔、单元类型，这三个类型所处的地位比较尴尬，你说它们重要吧，确实出现的身影不是很多，说它们不重要吧，有时候也是不可或缺，而且这三个类型都有一个共同点：简单</p>
</blockquote>

<h3 id="字符类型char">字符类型（char）</h3>

<ul>
  <li>所有 <code class="language-plaintext highlighter-rouge">Unicode</code>值都可以作为 Rust 的字符，包括单个的中文、日文、韩文、emoji 表情符号等等，都是合法的字符类型</li>
  <li>由于<code class="language-plaintext highlighter-rouge">Unicode</code>都是 4 个字节编码，因此字符类型也是占用 4 个字节</li>
  <li>和 js 不同，Rust 的字符只能用 <code class="language-plaintext highlighter-rouge">''</code>（单引号）来表示，<code class="language-plaintext highlighter-rouge">""</code>（双引号）是字符串！</li>
</ul>

<h3 id="布尔bool">布尔（bool）</h3>

<ul>
  <li>布尔值占用内存的<strong>大小为 <code class="language-plaintext highlighter-rouge">1</code>个字节</strong></li>
  <li>使用布尔类型的场景主要用于流程控制</li>
</ul>

<h3 id="单元类型">单元类型</h3>

<ul>
  <li>单元类型就是<code class="language-plaintext highlighter-rouge">()</code>，唯一的值也是<code class="language-plaintext highlighter-rouge">()</code>。</li>
  <li>main 函数返回的就是单元类型<code class="language-plaintext highlighter-rouge">()</code></li>
  <li>没有返回值的函数，在 Rust 中是有单独定义的：<code class="language-plaintext highlighter-rouge">发散函数（diverge function)</code>，顾名思义，无法收敛的函数</li>
  <li>常见的 <code class="language-plaintext highlighter-rouge">println!()</code>的返回值也是<code class="language-plaintext highlighter-rouge">()</code></li>
  <li>jser 可以理解为 void；hh</li>
  <li>和 Go 语言的 <em>struct{}</em>类似，可以作用一个值来占位，但是完全不占用任何内存</li>
</ul>

<h2 id="语句和表达式">语句和表达式</h2>

<ul>
  <li>可以求值的即为<strong>表达式</strong></li>
  <li>函数体内，前面的是<strong>语句</strong>（statement），最后一行是<strong>表达式</strong>（expression），需要能明确的区分这两个概念。（作者：对于很多其他语言来说，这两个往往无需区分。me：确实，js 不用，我有 return）</li>
  <li>Rust 的函数体，最后由一个表达式来返回值（多种情况下的返回呢？带着疑惑继续看
    <ul>
      <li>十几分钟后记：也可以使用<code class="language-plaintext highlighter-rouge">return</code>提前返回</li>
    </ul>
  </li>
  <li><strong>表达式不能用<code class="language-plaintext highlighter-rouge">;</code>结尾，否则会被认为是语句！</strong></li>
  <li>表达式如果不返回任何值，会隐式地返回一个<code class="language-plaintext highlighter-rouge">()</code>(单元类型)</li>
</ul>

<h2 id="函数">函数</h2>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fn</span> <span class="nf">add</span><span class="p">(</span><span class="n">i</span><span class="p">:</span> <span class="nb">i32</span><span class="p">,</span> <span class="n">j</span><span class="p">:</span> <span class="nb">i32</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">i32</span> <span class="p">{</span>
    <span class="n">i</span> <span class="o">+</span> <span class="n">j</span>
<span class="p">}</span>
</code></pre></div></div>

<ul>
  <li>该函数如此简单，但是又是如此的五脏俱全。声明函数的关键字<code class="language-plaintext highlighter-rouge">fn</code>（不用写 function 了 0.0），函数名、参数、参数类型、返回类型，</li>
</ul>

<p><img src="https://raw.githubusercontent.com/bran-nie/blog_images/images/20231016105651.png" alt="" /></p>

<h3 id="函数要点">函数要点</h3>

<ul>
  <li>函数名和变量名使用下划线格式（蛇形命名法），例如：<code class="language-plaintext highlighter-rouge">fn add_two() -&gt; {}</code></li>
  <li>函数的位置可以随便放，Rust 不关心我们在哪里定义了函数，只要有定义即可（类似 js 的函数第一公民，函数声明提升）</li>
  <li>每个函数参数都需要标注类型（如果默认值，是不是就不用标注了？带着疑惑继续看）</li>
</ul>

<h3 id="函数参数">函数参数</h3>

<ul>
  <li>Rust 是强类型语言，因此每一个参数都要标识它的具体类型</li>
</ul>

<h3 id="函数返回">函数返回</h3>

<ul>
  <li>
    <p>可以用 return 提前返回</p>
  </li>
  <li>
    <p>返回值是最后一条表达式的值</p>
  </li>
  <li>
    <p>函数也是表达式</p>
  </li>
  <li>
    <p>不要有分号</p>
  </li>
  <li>
    <p>特殊的返回类型</p>

    <ul>
      <li>
        <p>无返回值</p>

        <ul>
          <li>
            <p><code class="language-plaintext highlighter-rouge">()</code>是一个零长度的元组。</p>
          </li>
          <li>
            <p>函数没有返回值，那么返回一个<code class="language-plaintext highlighter-rouge">()</code></p>
          </li>
          <li>
            <p>通过<code class="language-plaintext highlighter-rouge">;</code>结尾的表达式返回一个<code class="language-plaintext highlighter-rouge">()</code></p>
          </li>
          <li>
            <p>示例</p>

            <div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fn</span> <span class="nf">cler</span><span class="p">(</span><span class="n">text</span><span class="p">:</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="nb">String</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="p">()</span> <span class="p">{</span>
    <span class="o">*</span><span class="n">text</span> <span class="o">=</span> <span class="nn">String</span><span class="p">::</span><span class="nf">from</span><span class="p">(</span><span class="s">""</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div>            </div>

            <p>显式的返回了<code class="language-plaintext highlighter-rouge">()</code></p>

            <div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">use</span> <span class="nn">std</span><span class="p">::</span><span class="nn">fmt</span><span class="p">::</span><span class="n">Debug</span><span class="p">;</span>

<span class="k">fn</span> <span class="n">report</span><span class="o">&lt;</span><span class="n">T</span><span class="p">:</span> <span class="n">Debug</span><span class="o">&gt;</span><span class="p">(</span><span class="n">item</span><span class="p">:</span> <span class="n">T</span><span class="p">)</span> <span class="p">{</span>
  <span class="nd">println!</span><span class="p">(</span><span class="s">"{:?}"</span><span class="p">,</span> <span class="n">item</span><span class="p">);</span>

<span class="p">}</span>
</code></pre></div>            </div>

            <p>report 隐式的返回一个 <code class="language-plaintext highlighter-rouge">()</code></p>
          </li>
        </ul>
      </li>
      <li>
        <p>永不返回的发散函数 <code class="language-plaintext highlighter-rouge">!</code></p>

        <ul>
          <li>
            <div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="k">fn</span> <span class="nf">dead_end</span><span class="p">()</span> <span class="k">-&gt;</span> <span class="o">!</span> <span class="p">{</span>
    <span class="nd">panic!</span><span class="p">(</span><span class="s">"你已经到了穷途末路，崩溃吧！"</span><span class="p">);</span>
  <span class="p">}</span>
</code></pre></div>            </div>
          </li>
          <li>
            <p>当用 <code class="language-plaintext highlighter-rouge">!</code> 作函数返回类型的时候，表示该函数永不返回( diverge function )</p>
          </li>
          <li>这种语法往往用做会导致程序崩溃的函数</li>
        </ul>
      </li>
    </ul>
  </li>
</ul>]]></content><author><name>Bran.nie</name></author><category term="Rust" /><summary type="html"><![CDATA[Rust 基础入门 - 01]]></summary></entry><entry><title type="html">将 Ubuntu 安装在 USB 移动硬盘上，随时随地使用</title><link href="https://blog.bran-nie.cn/2023/10/10/ubuntu-in-usb-disk/" rel="alternate" type="text/html" title="将 Ubuntu 安装在 USB 移动硬盘上，随时随地使用" /><published>2023-10-10T00:00:00+08:00</published><updated>2023-10-10T00:00:00+08:00</updated><id>https://blog.bran-nie.cn/2023/10/10/ubuntu-in-usb-disk</id><content type="html" xml:base="https://blog.bran-nie.cn/2023/10/10/ubuntu-in-usb-disk/"><![CDATA[<p>前段时间折腾了一下 <code class="language-plaintext highlighter-rouge">Ubuntu</code>系统，起因是家里的 Windows 除了作为游戏机，似乎也没别的作用了，平时又不想带 mac 回家时，想敲敲代码或者或者折腾东西时，Windows 环境一个是配置麻烦，另一个是也不好使，偶然间，就想到了 <code class="language-plaintext highlighter-rouge">Linux</code>系统。</p>

<p>原本是想着 Windows 装个双系统的，无奈中间遇到一些意外，没有装好，后来发现还有外置硬盘装系统这种操作，瞬间觉得很赞，因为我看到角落里遗落的那个来自<strong>废旧笔记本的固态硬盘</strong>！(没错，就是和 <a href="/2023/09/28/diy-display/" target="_blank">DIY 屏幕</a> 用的同一个笔记本，嘿嘿 😁)</p>

<h2 id="前置准备">前置准备</h2>

<ul>
  <li>USB 移动硬盘，推荐固态的（我的是 240G 的固态，笔记本拆卸，几十块钱买个硬盘盒，搞定</li>
  <li>U 盘，8G 及以上即可（注意 ⚠️：制作成启动盘时，会格式化 U 盘数据，请注意数据存储</li>
  <li>电脑一台（Windows、Mac OS）我所使用的是 Windows</li>
  <li><a href="https://cn.ubuntu.com/download/desktop" target="_blank">Ubuntu 官网镜像 </a>（我使用的是 22.04 LTS，23.04 我在安装的时候，似乎有问题，在分区的步骤时，扫描不出硬盘列表，omz）</li>
  <li>镜像烧录软件，推荐两种
    <ul>
      <li><a href="https://etcher.balena.io/" target="_blank">balenaEtcher</a>，多平台支持</li>
      <li><a href="https://rufus.ie/zh/" target="_blank">Rufus</a>，Windows</li>
    </ul>
  </li>
</ul>

<h2 id="制作-usb-启动盘">制作 USB 启动盘</h2>

<p>安装好烧录软件 balenaEtcher，也下载好镜像后，就可以制作启动盘了</p>

<p>将准备好的 U 盘插入电脑，打开软件，选择镜像，选择 u 盘，点击 Flash 进行烧录，等待即可。三板斧后，启动盘就制作好了</p>

<p><img src="/images/a40f15d2-select-iso.png" alt="img" /></p>

<h2 id="移动硬盘格式化">移动硬盘格式化</h2>

<p>格式化为 NTFS 即可，后续分区时还会继续操作</p>

<h2 id="设置-bios">设置 BIOS</h2>

<p>如果不知道怎么进入到 bios，可以根据电脑型号（笔记本）或者电脑主板型号进行搜索，常见的进入方式有：开机的同时，按住 F12 / F5 / F2 / Del 中的其中一个，具体是哪个，需要看品牌的。</p>

<p>请注意，Windows 电脑，一般默认打开了 <code class="language-plaintext highlighter-rouge">secure boot</code> 这个选项，会导致我们无法安装其他操作系统，比如 Ubuntu。因此 Windows 如果在安装 Ubuntu 中遇到问题，需要检查下面两点：</p>

<ol>
  <li>secure boot 是否开启，如果 able，则设置为 enable</li>
  <li>硬盘模式需要改为 achi 模式</li>
</ol>

<p>设置 bios 的系统启动顺序，我这里设置为 USB、USB disk、电脑本身的硬盘</p>

<h2 id="安装系统重点是分区">安装系统（重点是分区</h2>

<h3 id="进入安装流程">进入安装流程</h3>

<p>设置好 bios，将移动硬盘、USB 启动盘都插入到电脑后，开启电脑，可以进入到安装程序</p>

<p><img src="/images/image-20231011161742791.png" alt="img" /></p>

<p>选择第一个进入即可。</p>

<h3 id="选择安装步骤的语言">选择安装步骤的语言</h3>

<p>稍作等待，就可以进入到系统的安装流程界面了，首先是选择安装语言，这里我们在语言选择的滚动区域，找到简体中文即可。</p>

<h3 id="选择安装-ubuntu">选择安装 Ubuntu</h3>

<p>第一步，是让我们选择，是安装系统，还是试用系统，这里我们选择安装，当然，也可以先试试，不过之后，要重新进来。</p>

<h3 id="安装设置">安装设置</h3>

<p>这里选择正常安装即可，其他选择自由勾选</p>

<h3 id="安装类型">安装类型</h3>

<p>这里一定一定要注意，<strong>选择其他选项</strong>，除非不打算用原本的系统了（hhh</p>

<h3 id="分区">分区</h3>

<p>在出现的磁盘列表中，找到移动硬盘，如果不确定是哪个，可以根据磁盘的大小来确认</p>

<p><img src="/images/fenqu.jpg" alt="" /></p>

<p>选中移动磁盘后，在列表下方有个 + 号，可以给磁盘进行分区，可以参考下面的进行分区</p>

<p>分区之后，<strong>安装启动引导器的设备</strong>选择 efi 那个选项即可，随后点击现在安装，会让选择地区，设置用户名、账户、密码，设置好之后，点击继续，等待安装好。安装好之后，不要选择重启。</p>

<p>到这里后，是不是以为已经搞定了？嘿嘿，不是的，我试了两次，发现 Ubuntu 的引导是写入到 Windows 的系统盘上，这就导致想拿着移动硬盘到其他电脑上使用时，根本进入不到系统。。。</p>

<p>ok，怎么解决这个问题呢，在我们等待安装好之后，先不要重启系统，参考 <a href="https://help.ubuntu.com/community/Boot-Repair" target="_blank">Boot-Repair</a> 文章中的第二个选项，我们需要做的是：</p>

<ol>
  <li>
    <p>需要联网</p>
  </li>
  <li>
    <p>在 Ubuntu 系统中（安装好之后，不要重启）打开 terminal，输入以下命令：</p>
  </li>
</ol>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>add-apt-repository ppa:yannubuntu/boot-repair <span class="o">&amp;&amp;</span> <span class="nb">sudo </span>apt update
<span class="nb">sudo </span>apt <span class="nb">install</span> <span class="nt">-y</span> boot-repair <span class="o">&amp;&amp;</span> boot-repair
</code></pre></div></div>

<ol>
  <li>等命令执行成功之后，在出现的界面中，选择默认的选项继续就行了。</li>
</ol>

<p>这些操作，是将 Ubuntu 的引导移动到移动硬盘中。完成后，就可以重启了，也可以试着将移动硬盘插入到其他电脑试试效果。</p>

<h2 id="任意电脑使用">任意电脑使用</h2>

<p>当制作好移动硬盘的 Ubuntu 系统盘后，只需要将其插到一台 Mac/Windows 电脑上，就能够随时随地的使用了，移动电脑，哈哈哈。</p>

<ul>
  <li>Windows：进入到 bios，设置系统启动顺序，将 usb 设置在内置硬盘的前面，保存后重启即可
    <ul>
      <li>不同的电脑进入到 bios 方式不同，可以根据电脑/主板型号，搜索进入 bios 的方式</li>
    </ul>
  </li>
  <li>Mac：Mac 没有 bios 设置，u 盘启动，需要在开机的时候，长按 option 键，进入到磁盘选择界面，选择 Ubuntu 所在的移动硬盘即可</li>
</ul>]]></content><author><name>Bran.nie</name></author><category term="Blog" /><category term="Ubuntu" /><summary type="html"><![CDATA[将 Ubuntu 安装在 USB 移动硬盘上，随时随地使用]]></summary></entry><entry><title type="html">使用微积木 + 废旧笔记本屏幕 DIY 一个便携式显示器</title><link href="https://blog.bran-nie.cn/2023/09/28/diy-display/" rel="alternate" type="text/html" title="使用微积木 + 废旧笔记本屏幕 DIY 一个便携式显示器" /><published>2023-09-28T00:00:00+08:00</published><updated>2023-09-28T00:00:00+08:00</updated><id>https://blog.bran-nie.cn/2023/09/28/diy-display</id><content type="html" xml:base="https://blog.bran-nie.cn/2023/09/28/diy-display/"><![CDATA[<p>朋友给的废弃的笔记本，两个硬盘已经被我拆下来，分别是 240G 的固态，1T 的机械，网上买了两个硬盘盒，安装上就是两个移动硬盘了。那剩下还可以较大回收价值的就是那块三星的显示面板了。查了一下，淘一个显示器驱动板，就能当做一个显示器来使用了，那就开搞吧～</p>

<h2 id="最终效果展示">最终效果展示</h2>

<p>话不多说，先看看最终效果，激起兴趣再说</p>

<p>塞尔达 无敌！
<img src="/images/blog/IMG_7193.jpg" alt="" /></p>

<p>背面效果
<img src="/images/blog/IMG_7190.jpg" alt="" /></p>

<p>由于屏幕面板背后会亮，这样反而还能在晚上突出背面的图像
<img src="/images/blog/IMG_7189.jpg" alt="" /></p>

<h2 id="生命不息折腾不止">生命不息，折腾不止</h2>

<h3 id="显示屏">显示屏</h3>

<p>笔记本拆卸屏幕，如果不熟悉，怕拆坏，可以查看笔记本的型号，网上搜一下这个型号的拆机视频，先看一遍，知道大概流程后，再边看边拆就好了。需要注意的是，尽量不要大力出奇迹（不然可能大力出霉迹 omz</p>

<h3 id="驱动板">驱动板</h3>

<p>淘宝上搜一下笔记本显示屏驱动板，找一个有销量的店家，问问客服购买什么样的，客服会让我们拍一下笔记本屏幕背后的信息：品牌、型号等等，然后会给我们说哪些驱动板适用。选择一个下单购买就好了。</p>

<p>我的显示屏
<img src="/images/blog/IMG_5213.jpeg" alt="" /></p>

<p>一般一个完整的套件包括：驱动板、屏线、按键板及接线。（可选的电源</p>

<p>其中，驱动版上会有常见的视频接口，如：VGA、HDMI、DP 等等，可以根据自己的需求来。</p>

<p><img src="/images/IMG_5210.JPG" alt="" /></p>

<p><img src="/images/IMG_5211111.JPG" alt="" /></p>

<blockquote>
  <p>我这里第一次抱着垃圾佬的想法，买最便宜的用，VGA + HDMI 接口，电源输入是 12V DC 接口的，也只是打算硬纸板 + 双面胶固定，主打的就是能用就行。后来，网上冲浪时，发现还有人，用<strong>微积木</strong>搭建了一个屏幕外壳，我艹，好酷啊，颅内高潮了，不行，我也要。</p>

  <p>同时，看了看手上的厚厚的驱动板，做成便携式显示器后，如果想连接 switch，还需要：switch 电源及线、HDMI 线、显示器的电源及线。好吧，这似乎，也没便携式。</p>

  <p>想到店家还有一款驱动板，轻薄，支持 type-c 一线通，也是 type-c 供电，这不是完美搭配？除了钱包哭泣之外（要比上面那个贵一倍</p>
</blockquote>

<h3 id="8mm-微积木">8mm 微积木</h3>

<p>关于这个，我的是 15.6 寸的屏幕，大概需要 4000-5000 个积木，微积木也不贵，5500 大概 24 元左右，我是拼多多搜索，看销量排名，选择靠前的店铺购买的。</p>

<h4 id="tips">tips：</h4>

<ul>
  <li><strong>图案大小</strong>：先测量屏幕的长宽，计算出微积木的长、宽的个数，然后就可以大致确定图案的大小，我的：长 54 个积木，宽 34 个积木。</li>
  <li><strong>图案</strong>：可以自己找图片转像素图，也可以网上搜索像素图，也可以使用 Excel 自己画（下面会给我的示例</li>
  <li><strong>积木购买</strong>：通过图案的确定，可以购买对应的颜色，我所买的积木店铺，他们有 36 种颜色，完全足够了。</li>
  <li><strong>拼接</strong>：快速方案：可以先把同色积木，一个个拼，拼成一个条子，俗称撸积木条，这样在多色积木使用时，会比较方便，撸条子熟能生巧，几百个后，都可以不用看着积木，纯肌肉记忆进行拼接了。建议找部电影，hahah</li>
</ul>

<h3 id="excel-绘制图案">Excel 绘制图案</h3>

<p><img src="/images/20230928181512.png" alt="" /></p>

<p>新建一个 Excel 表格，将单元格的宽高设置成一样的，再将行列设置为显示器外壳计算出来的积木个数。填充颜色来进行像素图绘画吧～</p>

<p><img src="/images/20230928204630.png" alt="" /></p>

<h3 id="拼装">拼装</h3>

<p>驱动板的外壳</p>

<p>这个图中的屏线，就是错误示范，我刚开始是折叠了，后续安装完成后，使用时有雪花，再渐渐就变成了非正常显示了。
<img src="/images/blog/20230928205813.png" alt="" /></p>

<p><img src="/images/blog/20230928205614.png" alt="" /></p>

<p>驱动板外壳的图案，嘿嘿，问号砖块
<img src="/images/blog/20230928210239.png" alt="" /></p>

<h4 id="tips-1">tips：</h4>

<ul>
  <li>屏线可以弯折，但注意不要折叠，不然很容易就会出现干扰，表现就是屏幕出现雪花，严重的话就不能正常显示了。</li>
  <li>为了稳固一些，可以购买一瓶积木所用的胶水，安装完成，测试通过之后，给关键地方补上胶水，也就是每层连接的地方，这样就可以放心的移动了。</li>
</ul>

<h2 id="收获">收获</h2>

<p>比如给树莓派整个显示屏
<img src="/images/blog/20230928211108.png" alt="" /></p>

<p>比如这个爱心（五六千个积木盘下来，确实是费手指，都撸出茧子了 omz
<img src="/images/20230928211221.png" alt="" /></p>

<p>但，总体来说，还是一个蛮不错的经历，通过旧物回收使用 ♻️、制定方案、发现微积木 DIY 外壳而修改方案、绘制像素图、拼装微积木、拼装外壳等等这一系列的操作，让我收获了一个便携式显示屏，还拥有了一个不错的体验，满足折腾的心思，真的是<strong>生命不息，折腾不止</strong>～～～</p>]]></content><author><name>Bran.nie</name></author><category term="Blog" /><summary type="html"><![CDATA[使用微积木 DIY 一个便携式显示器]]></summary></entry><entry><title type="html">Old friend</title><link href="https://blog.bran-nie.cn/2023/08/21/Old-friend/" rel="alternate" type="text/html" title="Old friend" /><published>2023-08-21T00:00:00+08:00</published><updated>2023-08-21T00:00:00+08:00</updated><id>https://blog.bran-nie.cn/2023/08/21/Old-friend</id><content type="html" xml:base="https://blog.bran-nie.cn/2023/08/21/Old-friend/"><![CDATA[<p>去之前只是想着吃饭聊聊天，在约时间的时候，我提到了 奥本海默 这个电影，问她看过没，她说刚上映就看了，然后在和我沟通之后她买了两张电影票给我和女友。</p>

<hr />

<h3 id="往昔">往昔</h3>

<p>算一下也有八年了，当初是为了在香港用教育优惠购买 mbp，就在 QQ 上，一顿搜索香港的大学的同乡会，通过群去私聊，找到了她，没想到一说就 ok，愿意帮我买。
于是我去银行，兑换了一万多港币的现金，现金（现在回想，往事不堪回首啊哈哈哈），过关，去香港理工大学，约定的图书馆楼下（包玉刚图书馆）。</p>

<p>买电脑时候，当我拿出现金时，我还记得苹果工作人员有些诧异的表情，哈哈哈。随后第一次吃饭，吃的是石锅拌饭（很好吃啊，可能是第一次吃）</p>

<p>再次见面，是中间安利 mbp 的精彩操作，我将电脑带过去，创建新用户给她用，事后证明，确实好用啊。不然，怎么后面也入坑了呢。（嘿，这次也让我入坑一个餐厅：牛一）</p>

<p><strong>牛一</strong>，一个日式放题餐厅，也就是牛肉自助餐火锅店。这个店，我去了九次（可以说是十次，只不过，第十次时，那个店换位置了，但新的店也是牛肉放题），并且，作为一个合格的推销员，这九次，每次都是带不同的伙伴来吃的，虽然不知道他们后面是否还有去过。</p>

<p>再后来就是毕业时，过去给拍一些毕业照片，但我确实菜，没拍到什么好的照片。</p>

<p>有去香港时，也会联系，那个暖风机。</p>

<p>疫情三年，未曾过岸啊</p>

<hr />

<h3 id="今日">今日</h3>

<p>今年香港通关了，拿出尘封三年多的通行证，跑到自助机续签了一年。</p>

<p>五一去了一趟东京，本约定返程那天，在中环吃个午饭，结果自己左脚磨伤，右手指甲翻伤，又在羽田机场候机熬夜，略微发烧，就推后了。</p>

<p>直到八月，这才见面，好久不见，还是往日模样。</p>

<p>去之前，问了一下，有没有什么想吃的，我给带过去。</p>

<p>first，魔芋爽。（意料之外，哈哈哈，后来知道了原因</p>

<p>糕点是她的偏爱，不过，这次有了新欢（鲍师傅）忘了旧爱（HEY YO 的拿破仑）。</p>

<p>修改去港的路线，高铁前往，因为糕点所在的商场十点开门。</p>

<p>08.19 九点出门，时间安排得刚好，赶到鲍师傅，买了一些推荐的单品，出商场还下起了雨。但无阻，地铁至高铁，准时到达西九龙。</p>

<p>不过，过关人是真的好多，可能都是压抑了三年之久的需求，犹如潮水。因此让我耗时一个钟，迟到半小时，才到餐厅，没怎么体验过的西餐厅。</p>

<p>与上次记忆中的不同的是，她与服务员沟通时，粤语说的很好，在我这个听不懂的木耳朵面前，她仿佛是个广东香港人一般。</p>

<p>西餐，我个人感觉蛮不错，相对中餐的满桌盛宴，个个都是爱吃的，然后嘎嘎吃，最后吃撑了，西餐这种上菜方式，也蛮有益。</p>

<p>前菜、餐汤、面包、主菜、餐后甜品及饮品。刀与叉的配合中，展露一丝异域文明的优雅。</p>

<p>每道菜都间隔不长不短，不会让人因饥饿而期待下一个，也不会因品尝菜品而打断交流。并且，对于我这种想减肥却只控制摄入量的困难人士来说，这个间隔，可以让饱腹的信号，及时到达大脑。</p>

<p>啊，一次令人难以忘怀的午餐，哦对了，牛排五分熟，好吃量又少，话不多说，下单原切。</p>

<p>ok，end 午餐感想。</p>

<hr />

<h3 id="此时">此时</h3>

<p>好久不见，见面如故。</p>

<p>因为她是有所闻但第一次见到女朋友，聊了怎么和女友相识，两个人同是陕西老乡呢，隔了一个西安市。</p>

<p>嘿，都有长痘后不留神会扣痘痘的习惯。</p>

<p><strong>聊了未来</strong>，在犹豫是否保留大陆身份证，当然我的建议是留在香港呗，有想法再去其他地方。家里方面，叔叔是支持她的选择，阿姨倒是希望回去，按部就班，结婚生子，当然朋友不太愿意。不过，阿姨这样想也是情理之中，毕竟就这一个孩子嘛，肯定怕在外面受委屈，因此想其在身边，可以照顾。</p>

<p>她疑惑为什么爸妈的想法会是两个方向，还能没有矛盾，我解释说，可能因为你爸爸知道的比较多，而你妈妈只是更感性的爱你，总之，两个人对你的爱，不一样，但都是爱，所以没有什么大的矛盾。</p>

<p>即将要面临抉择了，签证和通行证今年会到期，到时候要选择哪个，我给了建议（hk），也告知了一些大陆的时事，希望能更坚定她 hk 的心思。</p>

<p>后续，计划给其爸妈一个看世界的方式，规划在未来一两周内完成。</p>

<p><strong>聊了生活</strong>，香港居，易或不易。从审计转做 xx 之后，工作方面，没有那么繁忙了。对应的生活方面，就渐为丰富，周末时候，可以朋友家蹭饭，可以相约逛街，可以自驾去海边听风，也可以小酌一杯，看场电影。
消费是高，但也足够，她的心态，和我相似，对于收入，够用就好啦，（PS：我还是要多考虑一些，毕竟 I’m a man）</p>

<p>也说到，我的周末与生活，未来与规划，Tokyo。</p>

<p>问了为什么第一个想到的是魔芋爽，她说之前新冠，味觉失灵，偶然间吃了魔芋爽，发现很好吃，因此就被种草。hhh</p>

<p>对于带过去的鲍师傅，我问，这些在 hk 估计得多少钱，回答之后，差不多是我购买的一倍哎。果然，香港消费高啊（相比深圳）（其实，都还正常，相比当地城市的收入）</p>

<p>提到，是我让她入坑了苹果系列，嗯，坦然承认，毕竟，自认为做了好事，hhh。</p>

<p>提到，什么时候有另一半，只叹大学时候没去考虑，毕业工作之后，不好遇到合适的人啊，不过在这方面，我是想法一致：不因压力，不循世俗，不惧孤独，不随主流，安好，就好。</p>

<p>感谢她请我们观看的《奥本海默》，让我新认识了一个历史人物。</p>

<blockquote>
  <p>也是第一次在香港看电影呢</p>
</blockquote>

<hr />

<h2 id="某刻">某刻</h2>

<p>现在兴起香港的反向吃吃喝喝，即香港不少年轻人，反向到深圳，吃吃喝喝玩玩，我看到都有人整理了一份各个区的美食 Excel，当然，还有避雷页面。</p>

<p>于是，再次将深圳见提上日程，深圳的全国美食，清远漂流，珠海温泉。嗯，三天两次，美食菜单诱惑。</p>

<p>也有，未来的许许多多的交集，这一切，都来源于曾经一个囊中羞涩，却又想一步到位，越价购买 mbp 的人，在 QQ 上一顿搜寻，认识的姑娘。</p>

<p>一个可爱的人儿，愿生活如暖风，温柔相伴。</p>]]></content><author><name>Bran.nie</name></author><category term="Life" /><summary type="html"><![CDATA[Old friend]]></summary></entry><entry><title type="html">十进制转化其他进制，原理分析</title><link href="https://blog.bran-nie.cn/2022/03/07/base-conversion/" rel="alternate" type="text/html" title="十进制转化其他进制，原理分析" /><published>2022-03-07T00:00:00+08:00</published><updated>2022-03-07T00:00:00+08:00</updated><id>https://blog.bran-nie.cn/2022/03/07/base-conversion</id><content type="html" xml:base="https://blog.bran-nie.cn/2022/03/07/base-conversion/"><![CDATA[<p>LeetCode 的每日一题，今天的题目是<a href="https://leetcode-cn.com/problems/base-7/" target="\_blank">“504. 七进制数”</a>，<strong>jser</strong> 看到这道题，简直 so easy，一行代码解决：<code class="language-plaintext highlighter-rouge">return num.toString(7)</code>。但是，对于想成为大佬的我们来说，不能局限于 API 工程师，除了灵活使用语言提供的 API 之外，我们也要去思考这背后的逻辑、原理是什么？毕竟，计算机也是由人们创造出来的可以高速运算的 01 工具。</p>

<p>OK，话不多说，让我们看看通过这个十进制转七进制这道题，如何实现，又如何通过理解其背后的逻辑、原理，然后举一反三，实现十进制到其他进制之间的转化！</p>

<h3 id="运行结果">运行结果</h3>

<p><img src="/images/20220307123550.png" alt="" /></p>

<h3 id="前置知识">前置知识</h3>

<blockquote>
  <p>面向对进制不熟悉的同学而写，了如指掌的同学可以跳过本段～～
<strong>n 进制，在数学中表示为数值从 0 累加到 n-1 后，再加 1，就要向更高位进 1 了。</strong> 即在我们熟知的十进制中，有着个位十位百位等等。
那么，个位中，有 0-9 来表示，当从 9 再加 1 时，0-9 中已经没有可以表示的数字了，那么这时候就会向更高位也就是十位进一，即 10。</p>

  <p>那么对于 7 进制呢？同样的思想，它也是会有“个位十位百位”，那么它的个位，就用 0-6 来表示了。同样的，在七进制中，6 再加 1 时，就要向更高位进一了，于是，十进制的 7 就是 七进制的 10。</p>
</blockquote>

<h3 id="十进制转七进制背后的逻辑分析">十进制转七进制，背后的逻辑分析</h3>

<ul>
  <li>对于十进制中的 0-6，可以不用变化就是七进制中的 0-6 了，为何？在上面提到，对于 n 进制来说，当数值累加到 n-1 之前时，是都不用进位的。</li>
  <li>对于十进制中的 7，6+1 即是 7，对于七进制，6 已经是个位能表示的最大值了，那不就是要向高位进一了吗？故十进制中的 7，由 6 + 1 后，向高位进一得到 <code class="language-plaintext highlighter-rouge">1-</code>，而 6 加完 1 后，也没有剩余值了，所以个位还是 0，故最终是：<code class="language-plaintext highlighter-rouge">10</code>。</li>
  <li>对于十进制中的 &gt; 7，其实就是前两者情况的复合情况。拿十进制中的 <code class="language-plaintext highlighter-rouge">10</code> 来说，可以用十进制的<code class="language-plaintext highlighter-rouge">3 + 7</code>来表示吧，那么，<code class="language-plaintext highlighter-rouge">3</code>不就是对应情况 1，<code class="language-plaintext highlighter-rouge">7</code>不就是对应的情况 2 吗？对于的七进制就是<code class="language-plaintext highlighter-rouge">13</code>。而 十进制的<code class="language-plaintext highlighter-rouge">20</code>，那就是<code class="language-plaintext highlighter-rouge">6 + 7 + 7</code>，其中，<code class="language-plaintext highlighter-rouge">6</code>对应的是情况 1，而剩余的两个<code class="language-plaintext highlighter-rouge">7</code>就是七进制中的<code class="language-plaintext highlighter-rouge">20</code>，咦，两个七是<code class="language-plaintext highlighter-rouge">20</code>，那三个呢，四个呢，7 个的时候呢？
    <ul>
      <li>七进制中，逢 6 进 1，对于十位上的 6，也是如此，所以，十位上有将要有 7 个时，也是要更高一位进一的，即<code class="language-plaintext highlighter-rouge">60 + 10 = 100</code></li>
      <li>故，我们在进行转化时，对于十位上出现的大于 6 的情况，也是要以情况 3 来处理的。唔，循环判断了。</li>
    </ul>
  </li>
</ul>

<p>那接下来让我们尝试用代码实现一下吧～</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/**
 * @param {number} num
 * @return {string}
 */</span>
<span class="kd">var</span> <span class="nx">convertToBase7</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">num</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// 符号，考虑到 num 为负值，在这里将其符号存起来，下面取值时将其取正值</span>
    <span class="kd">const</span> <span class="nx">sign</span> <span class="o">=</span> <span class="nx">num</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="p">?</span> <span class="dl">'</span><span class="s1">-</span><span class="dl">'</span> <span class="p">:</span> <span class="dl">''</span><span class="p">;</span>
    <span class="kd">let</span> <span class="nx">str</span> <span class="o">=</span> <span class="dl">''</span><span class="p">,</span>
        <span class="nx">lastVal</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">abs</span><span class="p">(</span><span class="nx">num</span><span class="p">);</span>
    <span class="c1">// 使用 do while，而不是 while，是因为循环体内的语句至少会执行一次。用 do while 更合适，可以减少一次 lastVal 的取值书写。</span>
    <span class="k">do</span> <span class="p">{</span>
        <span class="c1">// 对剩余数值进行求余，这样得到的值就是 情况 1，随后得到的值添加到 结果字符串前方即可。PS：添加到前方是因为更高位。</span>
        <span class="nx">str</span> <span class="o">=</span> <span class="p">(</span><span class="nx">lastVal</span> <span class="o">%</span> <span class="mi">7</span><span class="p">)</span> <span class="o">+</span> <span class="nx">str</span><span class="p">;</span>
        <span class="c1">// 随后求出剩余数值中还有多少个 7。</span>
        <span class="nx">lastVal</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">floor</span><span class="p">(</span><span class="nx">lastVal</span> <span class="o">/</span> <span class="mi">7</span><span class="p">);</span>
    <span class="p">}</span> <span class="k">while</span> <span class="p">(</span><span class="nx">lastVal</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">);</span>
    <span class="c1">// 直到剩余数值为零，结束循环。</span>

    <span class="c1">// 将符号添加到结果中。Done！</span>
    <span class="k">return</span> <span class="nx">sign</span> <span class="o">+</span> <span class="nx">str</span><span class="p">;</span>

    <span class="c1">// 现在我们不是只会用 API 的工程师了～～</span>
    <span class="c1">// return num.toString(7);</span>
<span class="p">};</span>
</code></pre></div></div>

<h3 id="十进制转-n-进制">十进制转 n 进制</h3>

<p>上面了解到十进制转七进制的背后逻辑后，那么，对于十进制转化为其他进制，是否能做到举一反三呢？</p>

<p>本质上，我们需要知道的有两点：</p>

<ol>
  <li>对于 n 进制，在其 n-1 时，再加 1，就要向高位进一了。</li>
  <li>对于任意位置(个位十位百位)，都可看成一种情况，那就是对于<code class="language-plaintext highlighter-rouge">&gt;= n</code>的数值，要进行进位，<code class="language-plaintext highlighter-rouge">&lt; n</code>的数值，添加到字符串的最前方即可。</li>
</ol>

<p>那么，来看看代码怎么实现吧～</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/**
 *
 * @param {number} num
 * @param {number?} radix
 * @returns string
 */</span>
<span class="c1">// 这里 radix 使用 ES6+ 的参数默认值</span>
<span class="kd">var</span> <span class="nx">convertToBaseN</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">num</span><span class="p">,</span> <span class="nx">radix</span> <span class="o">=</span> <span class="mi">7</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">sign</span> <span class="o">=</span> <span class="nx">num</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="p">?</span> <span class="dl">'</span><span class="s1">-</span><span class="dl">'</span> <span class="p">:</span> <span class="dl">''</span><span class="p">;</span>
    <span class="c1">// 对进制进行最大最小值判断。范围：2～32</span>
    <span class="nx">radix</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">max</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">min</span><span class="p">(</span><span class="nx">radix</span><span class="p">,</span> <span class="mi">32</span><span class="p">));</span>

    <span class="c1">// 声明多种进制个位对应的字符表示，如超过十进制的进制中，10 用 A 来表示</span>
    <span class="kd">const</span> <span class="nx">charIndex</span> <span class="o">=</span> <span class="nb">Array</span><span class="p">(</span><span class="mi">32</span><span class="p">)</span>
        <span class="p">.</span><span class="nx">fill</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
        <span class="p">.</span><span class="nx">map</span><span class="p">((</span><span class="nx">_</span><span class="p">,</span> <span class="nx">i</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">(</span><span class="nx">i</span> <span class="o">&lt;</span> <span class="mi">10</span> <span class="p">?</span> <span class="s2">`</span><span class="p">${</span><span class="nx">i</span><span class="p">}</span><span class="s2">`</span> <span class="p">:</span> <span class="nb">String</span><span class="p">.</span><span class="nx">fromCharCode</span><span class="p">(</span><span class="mi">65</span> <span class="o">+</span> <span class="nx">i</span> <span class="o">-</span> <span class="mi">10</span><span class="p">)));</span>

    <span class="c1">// 取 num 绝对值</span>
    <span class="kd">let</span> <span class="nx">str</span> <span class="o">=</span> <span class="dl">''</span><span class="p">,</span>
        <span class="nx">lastVal</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">abs</span><span class="p">(</span><span class="nx">num</span><span class="p">);</span>
    <span class="k">do</span> <span class="p">{</span>
        <span class="nx">str</span> <span class="o">=</span> <span class="nx">charIndex</span><span class="p">[</span><span class="nx">lastVal</span> <span class="o">%</span> <span class="nx">radix</span><span class="p">]</span> <span class="o">+</span> <span class="nx">str</span><span class="p">;</span>
        <span class="nx">lastVal</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">floor</span><span class="p">(</span><span class="nx">lastVal</span> <span class="o">/</span> <span class="nx">radix</span><span class="p">);</span>
    <span class="p">}</span> <span class="k">while</span> <span class="p">(</span><span class="nx">lastVal</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">);</span>

    <span class="k">return</span> <span class="nx">sign</span> <span class="o">+</span> <span class="nx">str</span><span class="p">;</span>
<span class="p">};</span>
</code></pre></div></div>

<h3 id="最后总结">最后总结</h3>

<p>Q：这题这么简单，有必要吧啦吧啦写这么多吗？
A：可能不少人会对这种简单题，嗤之以鼻，但简单题并非一无是处。对于我们了解知识，它其实是从外到内的一个很好的入口，<strong>不积跬步，无以至千里</strong>。当我们面对简单能够做到平心静气，那么算法中的中等题，困难题，不过是各种方式的排列组合，随之庖丁解牛罢了。</p>

<p>Q：有那么多大佬写了题解，你为啥还要巴拉巴拉的写呢？
A：无限的星空中，既有耀眼的恒星发散着它的光芒，也有着小小的星星，一闪一闪的照耀着。我们应当敬佩大佬，向之学习，也应当照耀前方，向前进！</p>

<p>Q: 那么我们应该？
A：冲啊，高山就在那里～</p>]]></content><author><name>Bran.nie</name></author><category term="Algorithm" /><summary type="html"><![CDATA[十进制转化其他进制，原理分析]]></summary></entry></feed>