<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>绿色记忆 &#187; Rust</title>
	<atom:link href="https://blog.gmem.cc/category/work/rust/feed" rel="self" type="application/rss+xml" />
	<link>https://blog.gmem.cc</link>
	<description></description>
	<lastBuildDate>Mon, 27 Apr 2026 13:32:34 +0000</lastBuildDate>
	<language>en-US</language>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.9.14</generator>
	<item>
		<title>Rust学习笔记</title>
		<link>https://blog.gmem.cc/rust-study-note</link>
		<comments>https://blog.gmem.cc/rust-study-note#comments</comments>
		<pubDate>Thu, 20 Jun 2019 01:50:06 +0000</pubDate>
		<dc:creator><![CDATA[Alex]]></dc:creator>
				<category><![CDATA[Rust]]></category>

		<guid isPermaLink="false">https://blog.gmem.cc/?p=27603</guid>
		<description><![CDATA[<p>简介 Rust是Mozilla主导的编译型通用编程语言，它的特点包括： 内存高效，不包含运行时和垃圾回收器（对比Go语言） 性能接近于C++开发的应用程序 可靠性，丰富的内存系统、所有权模型保证内存安全、线程安全 起步 基于rustup 基于rustup是官方推荐的安装方式，执行下面的命令即可： [crayon-69efd204acb65748018704/] 你需要根据向导提示完成Rust编译器、Rust包管理器（Cargo）的安装。 默认情况下，所有工具被安装到~/.cargo/bin目录中。 Hello Rust 使用fn关键字可以声明一个函数，名为main的函数可以作为入口点： [crayon-69efd204acb6a627382033/] 调用下面的命令可以构建二进制文件： [crayon-69efd204acb6c541006597/] 默认的编译结果位于当前目录，和源文件名字一致，可以直接执行。  Hello Cargo Cargo是Rust的构建系统和包管理器，通过它可以创建Rust项目、构建项目、下载并编译依赖库。 创建项目 <a class="read-more" href="https://blog.gmem.cc/rust-study-note">[...]</a></p>
<p>The post <a rel="nofollow" href="https://blog.gmem.cc/rust-study-note">Rust学习笔记</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></description>
				<content:encoded><![CDATA[<div class="wri_content_clear_both"><div class="blog_h1"><span class="graybg">简介</span></div>
<p>Rust是Mozilla主导的编译型通用编程语言，它的特点包括：</p>
<ol>
<li>内存高效，<span style="background-color: #c0c0c0;">不包含运行时和垃圾回收器</span>（对比Go语言）</li>
<li><span style="background-color: #c0c0c0;">性能接近于C++</span>开发的应用程序</li>
<li>可靠性，丰富的内存系统、所有权模型保证内存安全、线程安全</li>
</ol>
<div class="blog_h1"><span class="graybg">起步</span></div>
<div class="blog_h2"><span class="graybg">基于rustup</span></div>
<p>基于rustup是官方推荐的安装方式，执行下面的命令即可：</p>
<pre class="crayon-plain-tag">curl https://sh.rustup.rs -sSf | sh</pre>
<p>你需要根据向导提示完成<span style="background-color: #c0c0c0;">Rust编译器</span>、Rust<span style="background-color: #c0c0c0;">包管理器（Cargo）</span>的安装。 默认情况下，所有工具被安装到<span style="color: #000000;">~/.cargo/bin目录中。</span></p>
<div class="blog_h2"><span class="graybg">Hello Rust</span></div>
<p>使用fn关键字可以声明一个函数，名为main的函数可以作为入口点：</p>
<pre class="crayon-plain-tag">fn main() {
    // 调用宏需要用!
    println!("Hello, Rust!");
}</pre>
<p>调用下面的命令可以构建二进制文件：</p>
<pre class="crayon-plain-tag">rustc main.rs</pre>
<p>默认的编译结果位于当前目录，和源文件名字一致，可以直接执行。 </p>
<div class="blog_h2"><span class="graybg">Hello Cargo</span></div>
<p>Cargo是Rust的构建系统和包管理器，通过它可以<span style="background-color: #c0c0c0;">创建Rust项目、构建项目、下载并编译依赖库</span>。</p>
<div class="blog_h3"><span class="graybg">创建项目</span></div>
<p>调用下面的命令创建新的Cargo项目：</p>
<pre class="crayon-plain-tag">cargo new hello_cargo</pre>
<p>项目的布局如下：</p>
<pre class="crayon-plain-tag">├── Cargo.lock
├── Cargo.toml       # Cargo项目元数据文件
├── src              # 源码目录
│   └── main.rs
├── .gitignore       # 自动创建此文件
├── .git             # 自动创建Git仓库
└── target           # 存放构建结果
    └── debug</pre>
<div class="blog_h3"><span class="graybg">Cargo.toml</span></div>
<p>此文件格式为 TOML (Tom's Obvious, Minimal Language) ，声明Cargo项目的元数据、依赖：</p>
<pre class="crayon-plain-tag">[package]
name = "hello_cargo"
version = "0.1.0"
authors = ["Alex Wong <alex@gmem.cc>"]
edition = "2018"

[dependencies]</pre>
<p>第二个段dependencies用于声明依赖，在<span style="background-color: #c0c0c0;">Rust中代码包被称为crate</span>，此项目没有依赖，因此dependencies段中没有crate。</p>
<div class="blog_h3"><span class="graybg">构建</span></div>
<pre class="crayon-plain-tag"># 默认构建出调试版本：./target/debug/hello_cargo
cargo build

# 构建发布版本，耗时更多，代码更加优化
cargo build --release</pre>
<div class="blog_h3"><span class="graybg">运行</span></div>
<p>执行下面的命令构建并运行：</p>
<pre class="crayon-plain-tag">cargo run</pre>
<div class="blog_h3"><span class="graybg">检查</span></div>
<p>执行下面的命令可以快速的检查代码是否通过编译：</p>
<pre class="crayon-plain-tag">cargo check</pre>
<div class="blog_h1"><span class="graybg">语言</span></div>
<div class="blog_h2"><span class="graybg">数据类型</span></div>
<p>Rust的数据类型分为两大类：标量（scalar）和复合（compound）。</p>
<p>标量类型包括：</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">标量类型</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td class="blog_h3">整型</td>
<td>
<p>8-bit整数： <pre class="crayon-plain-tag">i8</pre>（有符号） <pre class="crayon-plain-tag">u8</pre>（无符号）<br />16-bit整数： <pre class="crayon-plain-tag">i16</pre> <pre class="crayon-plain-tag">u16</pre><br />32-bit整数： <pre class="crayon-plain-tag">i32</pre> <pre class="crayon-plain-tag">u32</pre><br />64-bit整数： <pre class="crayon-plain-tag">i64</pre> <pre class="crayon-plain-tag">u64</pre><br />其长度依赖于体系结构的整数：isize usize。在64bit机器上为64位</p>
<p>直接量语法：</p>
<pre class="crayon-plain-tag"># 除了byte类型（u8）之外，都可以加类型后缀
57u8 

# 可以使用_作为分隔符，以方便阅读
1_000

# 不同进制表示
0xff         # 16进制
0o77         # 8进制
0b1111_0000  # 2进制

# Byte（u8）特殊表示法
b'A'</pre>
<p> 关于整型溢出：u8可以存放的最大数为255，如果为其赋值256：</p>
<ol>
<li>debug编译模式下，导致Panic</li>
<li>release编译模式下，不会进行溢出检测。256会变成0</li>
</ol>
</td>
</tr>
<tr>
<td class="blog_h3">浮点型</td>
<td>Rust支持两种精度的浮点数<pre class="crayon-plain-tag">f32</pre> 、<pre class="crayon-plain-tag">f64</pre>。</td>
</tr>
<tr>
<td>布尔型</td>
<td><pre class="crayon-plain-tag">bool</pre>，<pre class="crayon-plain-tag">true</pre>或者<pre class="crayon-plain-tag">false</pre></td>
</tr>
<tr>
<td class="blog_h3">字符类型</td>
<td><pre class="crayon-plain-tag">char</pre>，使用单引号包围，代表了一个Unicode标量值，任何Unicode字符均可以表示</td>
</tr>
</tbody>
</table>
<p>复合类型包括：</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">复合类型</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td class="blog_h3">元组类型</td>
<td>将多个其它类型组合在一起：</p>
<pre class="crayon-plain-tag">let tup: (i32, f64, u8) = (500, 6.4, 1);</pre>
<p>要获取元组的单个元素，可以进行解构（destructure）操作：</p>
<pre class="crayon-plain-tag">let (x, y, z) = tup;</pre>
<p>或者，使用索引值来访问元素：</p>
<pre class="crayon-plain-tag">let five_hundred = x.0;
let six_point_four = x.1;</pre>
</td>
</tr>
<tr>
<td class="blog_h3">数组类型</td>
<td>类似于元组，但是每个元素的类型都必须相同：
<pre class="crayon-plain-tag">let a = [1, 2, 3, 4, 5];</pre>
<p>数组的类型名语法很特殊：<pre class="crayon-plain-tag">[type; number]</pre>，例如：</p>
<pre class="crayon-plain-tag">let a: [i32; 5] = [1, 2, 3, 4, 5];</pre>
<p>数组元素的个数不可变化。访问数组元素的语法不同于元组：</p>
<pre class="crayon-plain-tag">let first = a[0];
let second = a[1];</pre>
</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">关键字</span></div>
<div class="blog_h3"><span class="graybg">use</span></div>
<p>默认情况下，Rust将 <a href="https://doc.rust-lang.org/std/prelude/index.html">prelude</a>模块中定义的类型自动引入到所有程序的作用域中。不再prelude的类型，可以使用use语句显式引入作用域：
<pre class="crayon-plain-tag">use std::io;
io::stdin().read_line(&amp;mut guess).expect("Failed to read line");

// 如果不用use，也可以使用全限定名称引用：
std::io::stdin().read_line(...);</pre>
<div class="blog_h3"><span class="graybg">let mut</span></div>
<p>此关键字用于定义一个可变变量 ：</p>
<pre class="crayon-plain-tag">// 不需要声明变量类型
let mut guess = String::new();</pre>
<div class="blog_h3"><span class="graybg">let</span></div>
<p>此关键字用于定一个不可变变量。</p>
<pre class="crayon-plain-tag">let foo = bar;</pre>
<div class="blog_h3"><span class="graybg">.</span></div>
<p>可以用于基于索引来访问元组的元素：</p>
<pre class="crayon-plain-tag">let five_hundred = x.0; </pre>
<p>以及用来访问结构的方法、结构的字段。</p>
<div class="blog_h3"><span class="graybg">:</span></div>
<p>可以用于显式的声明变量类型：</p>
<pre class="crayon-plain-tag">let f: bool = false;</pre>
<div class="blog_h3"><span class="graybg">::</span></div>
<p>此操作符用于访问类型的静态成员：</p>
<pre class="crayon-plain-tag">let mut guess = String::new();  // 关联函数，也叫静态方法</pre>
<p>很多类型上都有<span style="background-color: #c0c0c0;">名为new的关联函数</span>，这种命名是Rust的一种惯用方式，用于创建类型的实例。</p>
<div class="blog_h3"><span class="graybg">&amp;</span></div>
<p>此关键字用于获得变量的引用：</p>
<pre class="crayon-plain-tag">io::stdin().read_line(&amp;mut guess);</pre>
<p>和变量类似，<pre class="crayon-plain-tag">&amp;mut guess</pre>表示可变引用，<pre class="crayon-plain-tag">&amp;guess</pre>则表示不可变引用。 </p>
<div class="blog_h3"><span class="graybg">!</span></div>
<p>用于调用宏：</p>
<pre class="crayon-plain-tag">println!("You guessed: {}", guess);</pre>
<div class="blog_h3"><span class="graybg">[]</span></div>
<p>声明数组类型、数组直接量、访问数组元素时使用该关键字。</p>
<div class="blog_h3"><span class="graybg">()</span></div>
<p>声明元组类型、数组直接量时使用该关键字。</p>
<p>声明函数形参、调用函数时，使用该关键字。</p>
<div class="blog_h3"><span class="graybg">//</span></div>
<p>单行注释。</p>
<div class="blog_h3"><span class="graybg">#</span></div>
<p>用于声明注解。</p>
<div class="blog_h2"><span class="graybg">函数</span></div>
<p>关键字<pre class="crayon-plain-tag">fn</pre>用于定义一个新函数。在Rust中函数、变量名都使用snake case风格 —— 全部小写、使用下划线分隔单词。</p>
<p>函数声明示例：</p>
<pre class="crayon-plain-tag">fn another_function(x: i32) {
    println!("The value of x is: {}", x);
}</pre>
<p>带有返回值的函数声明示例：</p>
<pre class="crayon-plain-tag">fn five() -> i32 {
    5
}</pre>
<p>使用 <pre class="crayon-plain-tag">return</pre> 关键字和指定值，可从函数中提前返回；但大部分函数隐式的返回最后的表达式 。</p>
<div class="blog_h3"><span class="graybg">多值返回</span></div>
<p>要想返回多个值，可以使用元组：</p>
<pre class="crayon-plain-tag">fn calculate_length(s: String) -> (String, usize) {
    let length = s.len();
    (s, length)
} </pre>
<div class="blog_h2"><span class="graybg">控制流</span></div>
<div class="blog_h3"><span class="graybg">if</span></div>
<p>不需要括号，类似于Go语言：</p>
<pre class="crayon-plain-tag">if number % 4 == 0 {
    println!("number is divisible by 4");
} else if number % 3 == 0 {
    println!("number is divisible by 3");
} else {
    println!("number is not divisible by 4, 3, or 2");
}</pre>
<p>if语句可以用在let语句中：</p>
<pre class="crayon-plain-tag">let condition = true;
let number = if condition {
    5
} else {
    6
};</pre>
<div class="blog_h3"><span class="graybg">loop </span></div>
<p>重复执行直到break为止：</p>
<pre class="crayon-plain-tag">let mut counter = 0;

let result = loop {
    counter += 1;

    if counter == 10 {
        break counter * 2;
    }
};</pre>
<p>可以看到，loop语句和if类似，可以用作let表达式的右值。 </p>
<div class="blog_h3"><span class="graybg">while</span></div>
<pre class="crayon-plain-tag">let mut number = 3;

while number != 0 {
    println!("{}!", number);

    number = number - 1;
}</pre>
<div class="blog_h3"><span class="graybg">for</span></div>
<p>可以用来方便的迭代集合元素：</p>
<pre class="crayon-plain-tag">let a = [10, 20, 30, 40, 50];

for element in a.iter() {
    println!("the value is: {}", element);
}</pre>
<div class="blog_h2"><span class="graybg">所有权</span></div>
<p>所有权（ownership）系统是 Rust 最独特的功能，其令 <span style="background-color: #c0c0c0;">Rust 无需垃圾回收（garbage collector）即可保障内存安全</span>，同时不需要容易出错的手工内存管理。</p>
<div class="blog_h3"><span class="graybg">何为所有权</span></div>
<p>对于每个Rust中的值，以下规则使用：</p>
<ol>
<li>Rust 中的每一个值都有一个被称为其 所有者（owner）的变量</li>
<li>值有且只有一个所有者</li>
<li>当所有者（变量）离开作用域，这个值将被丢弃</li>
</ol>
<p>以String类型为例说明，和字符串直接量不同，它可以变化：</p>
<pre class="crayon-plain-tag">let mut s = String::from("hello");
s.push_str(", world!");   // 追加字符串</pre>
<p>字符串直接量不可改变，在编译期长度可知， 因而可以在栈上分配，随着栈帧的销毁自动回收内存。</p>
<p>为了支持运行时动态改变，String类型是在堆上分配（编译时未知长度的内存空间），并在不需要String变量后将内存归还给OS。传统编程语言，要么手工释放内存，要么使用垃圾回收器，Rust使用了不同的方式处理内存释放：</p>
<pre class="crayon-plain-tag">{
    let s = String::from("hello"); // 从此处起，s 是有效的
    // 使用 s
}                                  // 此作用域已结束，
                                   // s 不再有效</pre>
<p>当变量离开作用域后，Rust自动调用一个特殊的<pre class="crayon-plain-tag">drop</pre>函数，完成内存的释放。</p>
<p>变量的所有权总是遵循相同的模式：</p>
<ol>
<li>将值赋给另一个变量时移动它</li>
<li>当持有堆中数据值的变量离开作用域时，其值将通过 drop 被清理掉，除非数据被移动为另一个变量所有</li>
</ol>
<div class="blog_h3"><span class="graybg">移动语义</span></div>
<p>考虑下面的语句：</p>
<pre class="crayon-plain-tag">let s1 = String::from("hello");
let s2 = s1;                     // 移动语义

println!("{}, world!", s1);      // Error</pre>
<p>将<span style="background-color: #c0c0c0;">s1“赋值”（移动）给s2</span>后，Rust认为s1不再有效，因此第三个语句会报错：value used here after move。这和其它语言非常的不同。</p>
<p>由于s1已经无效，Rust就不会尝试调用<pre class="crayon-plain-tag">drop</pre>函数，也就不会遇到二次释放（double free）问题。</p>
<p>一个设计选择是：<span style="background-color: #c0c0c0;">Rust 永远也不会自动创建数据的 “深拷贝”</span>。因此，任何自动的复制可以被认为对运行时性能影响较小。</p>
<div class="blog_h3"><span class="graybg">复制语义</span></div>
<p>Rust包含一个叫Copy trait的特殊注解，可以应用在类似整型这样的存储在栈上的类型上。如果一个类型拥有 Copy trait，<span style="background-color: #c0c0c0;">一个旧的变量在将其赋值给其他变量后仍然可用——也就是说这种类型的变量的赋值操作具有复制语义</span>，这相当于对上节的移动语义做了例外：</p>
<pre class="crayon-plain-tag">let x = 5;
let y = x;                          // 复制语义

println!("x = {}, y = {}", x, y);   // OK</pre>
<p>Rust 不允许<span style="background-color: #c0c0c0;">自身或其任何部分</span>实现了<span style="background-color: #c0c0c0;"> Drop trait 的类型使用 Copy trait</span>。 </p>
<p>使用复制语义的类型包括：</p>
<ol>
<li>整数</li>
<li>布尔</li>
<li>浮点数</li>
<li>字符</li>
<li>元组（所有元素均支持复制语义的话）</li>
</ol>
<div class="blog_h3"><span class="graybg">克隆操作</span></div>
<p>如果的确需要实现类似于C++/Go语言的赋值语义，可以使用通用的<pre class="crayon-plain-tag">clone</pre>函数：</p>
<pre class="crayon-plain-tag">let s1 = String::from("hello");
let s2 = s1.clone();

println!("s1 = {}, s2 = {}", s1, s2);</pre>
<div class="blog_h3"><span class="graybg">所有权和函数</span></div>
<p>将值传递给函数时的移动、赋值语义，和变量赋值时类似：</p>
<pre class="crayon-plain-tag">fn main() {
    let s = String::from("hello");  // s 进入作用域

    takes_ownership(s);             // s 的值移动到函数里 ...
                                    // ... 所以到这里不再有效

    let x = 5;                      // x 进入作用域

    makes_copy(x);                  // x 应该移动函数里，
                                    // 但 i32 是 Copy 的，所以在后面可继续使用 x

} // 这里, x 先移出了作用域，然后是 s。但因为 s 的值已被移走，
  // 所以不会有特殊操作

fn takes_ownership(some_string: String) { // some_string 进入作用域
    println!("{}", some_string);
} // 这里，some_string 移出作用域并调用 `drop` 方法。占用的内存被释放

fn makes_copy(some_integer: i32) { // some_integer 进入作用域
    println!("{}", some_integer);
} // 这里，some_integer 移出作用域。不会有特殊操作</pre>
<p>可以注意到，在同一作用域内<span style="background-color: #c0c0c0;">变量的销毁顺序，和它的声明顺序相反</span>。 </p>
<p>和传参类似，返回值也牵涉到所有权的转移：</p>
<pre class="crayon-plain-tag">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() -> String {             // gives_ownership 将返回值移动给
                                             // 调用它的函数

    let some_string = String::from("hello"); // some_string 进入作用域.

    some_string                              // 返回 some_string 并移出给调用的函数
}

// takes_and_gives_back 将传入字符串并返回该值
fn takes_and_gives_back(a_string: String) -> String { // a_string 进入作用域

    a_string  // 返回 a_string 并移出给调用的函数
}</pre>
<div class="blog_h3"><span class="graybg">引用和借用</span></div>
<p>一个变量传递给函数后，在调用者处就无法使用，除非函数再次将其返回：</p>
<pre class="crayon-plain-tag">fn main() {
    let s1 = String::from("hello");
    let (s2, len) = calculate_length(s1);
    println!("The length of '{}' is {}.", s2, len);  // 调用函数后仍然需要使用s1指向的字符串
}

fn calculate_length(s: String) -> (String, usize) {
    let length = s.len();
    (s, length)  // 为了满足仍然需要使用的需求，需要将s的所有权再次转移回去
}</pre>
<p>这种方式很罗嗦，因此Rust提供引用（references）功能来简化之：</p>
<pre class="crayon-plain-tag">//                     以String的引用为形参，不获得入参的所有权
fn calculate_length(s: &amp;String) -> usize {
    s.len()
}</pre>
<p><span style="background-color: #c0c0c0;">引用允许你使用值但不获取其所有权</span>。 没有所有权，则离开作用域时<span style="background-color: #c0c0c0;">就不会调用drop进行清理</span>。</p>
<p>Rust中将<span style="background-color: #c0c0c0;">获取引用作为函数参数称为 借用</span>（borrowing）。 默认情况下，借用的变量是不支持修改的，除非使用mut关键字：</p>
<pre class="crayon-plain-tag">//                     可变引用
fn change(some_string: &amp;mut String) {
    some_string.push_str(", world");
}</pre>
<p>可变引用有一个很大的限制：<span style="background-color: #c0c0c0;">在特定作用域中的特定数据有且只有一个可变引用</span>。这些代码会失败：</p>
<pre class="crayon-plain-tag">let mut s = String::from("hello");

let r1 = &amp;mut s;
let r2 = &amp;mut s;   // 错误</pre>
<p>同一作用域中，同时使用可变、不可变引用也不支持：</p>
<pre class="crayon-plain-tag">let r1 = &amp;s;
let r2 = &amp;s;       // 多个不可变引用是允许的
let r3 = &amp;mut s;   // 混合使用可变、不可变引用不允许</pre>
<p>再不同作用域中，使用两个可变引用则没有问题：</p>
<pre class="crayon-plain-tag">{
    let r1 = &amp;mut s;
} // r1 在这里离开了作用域，所以我们完全可以创建一个新的引用
let r2 = &amp;mut s;</pre>
<div class="blog_h3"><span class="graybg">悬垂引用</span></div>
<p>在具有指针的语言中，很容易通过释放内存时保留指向它的指针而错误地生成一个 悬垂指针（dangling pointer）—— 其<span style="background-color: #c0c0c0;">指向的内存可能已经被分配给其它持有者</span>。 </p>
<p>在Rust中，编译期检查可以避免悬垂引用：</p>
<pre class="crayon-plain-tag">fn dangle() -> &amp;String { // dangle 返回一个字符串的引用
    let s = String::from("hello"); // s 是一个新字符串
    &amp;s // 返回字符串 s 的引用
} // 这里 s 离开作用域并被丢弃。其内存被释放
  // 危险！</pre>
<p>这个例子中s是在函数体内创建，<span style="background-color: #c0c0c0;">dangle具有它的所有权，返回&amp;s仅仅是借用，而不获得s的所有权，这导致dangle函数结束时s被销毁</span>，返回的是悬垂引用。编译器会识别这个错误。 </p>
<p>解决这个错误的方法很简单，那就是返回s而非它的引用，转移掉所有权：</p>
<pre class="crayon-plain-tag">fn no_dangle() -> String {
    let s = String::from("hello");
    s
}</pre>
<div class="blog_h2"><span class="graybg">切片</span></div>
<p>另一个没有所有权的数据类型是 slice。<span style="background-color: #c0c0c0;">slice 允许你引用集合中一段连续的元素序列，而不用引用整个集合</span>：</p>
<pre class="crayon-plain-tag">let s = String::from("hello world");

// 切片语法，不包含结束索引
let hello = &amp;s[0..5];
let world = &amp;s[6..11];

// 包含结束索引
let hello = &amp;s[0..=4];
let world = &amp;s[6..=10];

// 从0开始
let slice = &amp;s[..2];
// 直到结尾
let slice = &amp;s[3..];
// 整个字符串
let slice = &amp;s[..];</pre>
<p>切片的本质是引用，对字符串的一部分的引用。 </p>
<div class="blog_h2"><span class="graybg">结构</span></div>
<p>结构是一种自定义数据类型，和元组不同，结构的每个部分——字段——都具有名字：</p>
<pre class="crayon-plain-tag">struct User {
    username: String,
    email: String,
    sign_in_count: u64,
    active: bool,
}</pre>
<p>创建结构实例的语法如下：</p>
<pre class="crayon-plain-tag">let mut alex = User {
    name: "Alex Wong".to_string(),
    age: 32,
};</pre>
<p>要访问结构字段，可以使用点号：</p>
<pre class="crayon-plain-tag">alex.age = 33;</pre>
<div class="blog_h3"><span class="graybg">从既有实例创建</span></div>
<pre class="crayon-plain-tag">let mut alex = User {
    first_name: "Alex".to_string(),
    last_name: String::from("Wong"),
    age: 32,
};

let mut bo = User {
    first_name: "ChangBo".to_string(),
    age: 3,
    // 其它字段从alex这个实例复制
    ..alex
};</pre>
<div class="blog_h3"><span class="graybg">元组结构体 </span></div>
<p>元组结构体类似于元组，但是限定每个成员的类型，类似于结构，但是<span style="background-color: #c0c0c0;">不指定成员的名称</span>： </p>
<pre class="crayon-plain-tag">struct Color(i32, i32, i32);
struct Point(i32, i32, i32);

let black = Color(0, 0, 0);
let origin = Point(0, 0, 0);</pre>
<p>类似于元组，元组结构体支持解构操作、点号+索引访问成员。 </p>
<div class="blog_h3"><span class="graybg">类单元结构体 </span></div>
<p>即unit-like structs，这是<span style="background-color: #c0c0c0;">没有任何字段的结构体</span>。它们类似于<pre class="crayon-plain-tag">()</pre>即unit类型。当你需要为某个类型实现trait但却不需要在类型中存储任何数据时，可以使用类单元结构体。</p>
<div class="blog_h3"><span class="graybg">所有权</span></div>
<p>结构体的字段，如果是非引用类型，则结构体本身对其拥有所有权。</p>
<p>如果字段是引用类型，则需要使用生命周期（lifetimes）这一特性，确保结构体引用数据的有效性和结构体本身一致。</p>
<div class="blog_h2"><span class="graybg">方法</span></div>
<p>Rust支持为结构体添加行为，这种行为叫做方法，即位于结构体（以及下文讨论的枚举、Trait）的上下文中定义的函数。方法的第一个参数可以是：</p>
<ol>
<li><pre class="crayon-plain-tag">self</pre>，指向结构体实例</li>
<li><pre class="crayon-plain-tag">&amp;self</pre>，执行结构体实例的引用</li>
</ol>
<p>方法语法如下：</p>
<pre class="crayon-plain-tag">// 这是一个注解，表示为下面的结构体保留调试信息
#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}

// 为结构体添加方法
impl Rectangle {
    // 第一个入参是结构的引用
    fn area(&amp;self) -> u32 {
        self.width * self.height
    }
}

fn main() {
    let rect1 = Rectangle { width: 30, height: 50 };

    println!(
        "The area of the rectangle is {} square pixels.",
        // 使用点号语法访问结构方法
        rect1.area()
    );
}</pre>
<div class="blog_h3"><span class="graybg">自动引用和解引用</span></div>
<p>在调用方法时，Rust会自动进行获得引用、解引用操作，而不需要C++那样手工处理。</p>
<p>也就是说，当调用<pre class="crayon-plain-tag">object.something()</pre>时，Rust会按照需要添加<pre class="crayon-plain-tag">&amp;</pre>、<pre class="crayon-plain-tag">&amp;mut</pre>或者<pre class="crayon-plain-tag">*</pre>以匹配方法签名。</p>
<div class="blog_h2"><span class="graybg">关联函数</span></div>
<p>除了方法，你还可以在<pre class="crayon-plain-tag">Impl</pre>块中定义不以self作为入参的函数，这种函数即关联（和结构相关，但是非实例方法）函数（associated functions），类似于C++中的静态函数。</p>
<p>在Rust中通常使用关联函数实现返回结构实例的新实例的工厂函数：</p>
<pre class="crayon-plain-tag">impl Rectangle {
    // 关联函数，第一个参数不是self
    fn square(size: u32) -> Rectangle {
        Rectangle { width: size, height: size }
    }
}</pre>
<p>调用关联函数时，需要使用结构体名::的语法形式：<pre class="crayon-plain-tag">let sq = Rectangle::square(3);</pre></p>
<div class="blog_h2"><span class="graybg">枚举</span></div>
<p>定义枚举：</p>
<pre class="crayon-plain-tag">enum IpAddrKind {
    V4,
    V6,
}</pre>
<p>创建枚举实例：</p>
<pre class="crayon-plain-tag">let four = IpAddrKind::V4;
let six = IpAddrKind::V6;</pre>
<p>使用枚举，和普通类型一样：</p>
<pre class="crayon-plain-tag">fn route(ip_type: IpAddrKind) { }

struct IpAddr {
    kind: IpAddrKind,
    address: String,
}

let home = IpAddr {
    kind: IpAddrKind::V4,
    address: String::from("127.0.0.1"),
};</pre>
<div class="blog_h3"><span class="graybg">枚举字段</span></div>
<p>你可以将任何数据类型定义为枚举的字段，包括其它枚举。而且，每个<span style="background-color: #c0c0c0;">枚举成员的字段可以各不相同</span>：</p>
<pre class="crayon-plain-tag">enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    ChangeColor(i32, i32, i32),
}</pre>
<div class="blog_h3"><span class="graybg">枚举方法</span></div>
<p>和结构类似，枚举也可以有方法：</p>
<pre class="crayon-plain-tag">impl Message {
    fn call(&amp;self) {
    }
}</pre>
<div class="blog_h3"><span class="graybg">Option</span></div>
<p>Rust没有引入其它语言支持的<pre class="crayon-plain-tag">null</pre>，null的缺陷是，你无法像使用普通值那样使用null值，这会导致错误。但是，“控制”所表示的概念的确是有意义的，为了解决此问题，Rust标准库提供了Option枚举：</p>
<pre class="crayon-plain-tag">enum Option<T> {
    Some(T),
    None,
}</pre>
<p>由于Option的使用场景非常广泛，因此不需要显式导入即可使用Option及其成员： </p>
<pre class="crayon-plain-tag">let some_number = Some(5);
let some_string = Some("a string");
// 使用None时，必须指定泛型参数，因为Rust不能自动推断
let absent_number: Option<i32> = None;</pre>
<p>在对<pre class="crayon-plain-tag">Option<T></pre>中的<pre class="crayon-plain-tag">T</pre>进行操作时，必须进行类型转换，这时有机会处理None值的情况。 </p>
<div class="blog_h2"><span class="graybg">match控制流</span></div>
<p>类似于switch-case语句：</p>
<pre class="crayon-plain-tag">fn value_in_cents(coin: Coin) -> u32 {
    match coin {
        Coin::Penny => {
            println!("Lucky penny!");
            1
        },
        Coin::Nickel => 5,
        Coin::Dime => 10,
        Coin::Quarter => 25,
        // 枚举成员的字段，可以在语句中访问
        Coin::Quarter(state) => {
            println!("State quarter from {:?}!", state);
            25
        },
    }
}</pre>
<p>每个分支有两个部分：一个模式和一些代码，如果值匹配模式，则对应的代码就会执行。</p>
<p>Rust中的枚举必须穷尽所有case，无法列举则必须使用<pre class="crayon-plain-tag">_</pre>通配：</p>
<pre class="crayon-plain-tag">let some_u8_value = 0u8;
match some_u8_value {
    1 => println!("one"),
    3 => println!("three"),
    5 => println!("five"),
    7 => println!("seven"),
    // 类似于default语句
    _ => (),
}</pre>
<div class="blog_h2"><span class="graybg">if let控制流</span></div>
<p>可以用来简单的处理单个匹配，忽略其它匹配：</p>
<pre class="crayon-plain-tag">let some_u8_value = Some(0u8);
if let Some(3) = some_u8_value {
    println!("three");
}</pre>
<p>支持指定else分支：</p>
<pre class="crayon-plain-tag">if let Coin::Quarter(state) = coin {
    println!("State quarter from {:?}!", state);
} else {
    count += 1;
}</pre>
<div class="blog_h1"><span class="graybg">模块系统</span></div>
<p>和Rust模块系统相关的概念包括：</p>
<ol>
<li>包（Package）：是Cargo的功能，用于构建、测试、分享Crate。包具有Cargo.toml文件</li>
<li>Crate：一个模块的树状结构，提供库项目或者二进制项目</li>
<li>模块（Modules）+ use 关键字：用于控制作用域、路径的私有性</li>
<li>路径（path）：命名结构、函数、模块等</li>
</ol>
<div class="blog_h2"><span class="graybg">Crate</span></div>
<p>Crate是一个二进制或者库项目， Crate根（Crate root）是一个用来描述如何构建Crate的文件。<span style="background-color: #c0c0c0;">带有Cargo.toml文件的包用来描述如何构建一个或多个Crate</span>，<span style="background-color: #c0c0c0;">一个包中最多有一个库项目</span>。</p>
<p>执行命令<pre class="crayon-plain-tag">cargo new</pre>时，创建的是一个包。Cargo约定，如果Cargo.toml同级目录包含src/main.rs，则当前包具有一个和</p>
<div class="blog_h1"><span class="graybg">标准库</span></div>
<div class="blog_h2"><span class="graybg">基础类型</span></div>
<div class="blog_h3"><span class="graybg">Result</span></div>
<p>Rust标准库的很多函数会返回Result类型：</p>
<pre class="crayon-plain-tag">let mut input = String::new();
let mut result = io::stdin().read_line(&amp;mut input).expect("Error!");</pre>
<p><pre class="crayon-plain-tag">std::result::Result</pre>是一个泛型枚举，它的两个成员是Ok、Error，表示操作是否成功。各子模块有Result的子类，例如上例中的<pre class="crayon-plain-tag">io::Result</pre>。</p>
<p>io::Result具有expect方法，如果Result为Error则调用expect会导致程序崩溃，传递给expect的消息会展示给用户。否则，该方法返回Result的值。</p>
<p>&nbsp;</p>
</div><p>The post <a rel="nofollow" href="https://blog.gmem.cc/rust-study-note">Rust学习笔记</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.gmem.cc/rust-study-note/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
