mirror of
https://github.com/RustyCab/LearnRustEasy.git
synced 2026-02-03 10:13:23 +08:00
fix Rust rust code
This commit is contained in:
@@ -165,7 +165,7 @@ cargo new hello_world
|
||||
|
||||
默认情况下,main.rs文件中已经包含了一个简单的`Hello, World`程序,代码如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
在Rust中,将值和变量关联的过程称为绑定,变量的绑定可以使用`let`关键字,如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
let a = 1; // 将1绑定到变量a
|
||||
let mut b = 2; // 将2绑定到变量b
|
||||
let some_number = Some(2); // 将Some(2)绑定到some_number
|
||||
@@ -16,7 +16,7 @@ Rust中变量分为不可变变量和可变变量。不可变变量不能对其
|
||||
|
||||
- 不可变变量定义方式如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
let a: u32 = 1; //将1绑定到a这个变量
|
||||
let b = 0u32;
|
||||
let c = 1; //定义时不指定类型,可以自动类型推导
|
||||
@@ -24,14 +24,14 @@ Rust中变量分为不可变变量和可变变量。不可变变量不能对其
|
||||
|
||||
对不可变变量二次绑定一个值会报错:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
let a: u32 = 1; //将1绑定到变量a,a为不可变变量,
|
||||
a = 2; //编译错误,a是不可变的变量,不能进行二次绑定
|
||||
```
|
||||
|
||||
- 可变变量定义方式如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
let mut a: u32 = 1; //通过mut关键字定义可变变量
|
||||
a = 2; //将2绑定到变量a,编译正确,因为a是可变变量,可以进行二次绑定
|
||||
let mut b = 2;
|
||||
@@ -45,14 +45,14 @@ Rust中变量分为不可变变量和可变变量。不可变变量不能对其
|
||||
|
||||
常量是绑定到一个名称不允许改变的值,定义方式如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
const HOUR_IN_SECONDS: u32 = 60 * 60;
|
||||
```
|
||||
|
||||
常量和不可变变量的区别:
|
||||
- 不允许对常量使用mut关键字,它总是不可变的,定义时必须显式的标注类型;
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
let a = 1u32; //编译正确
|
||||
let a = 1; //编译正确
|
||||
|
||||
@@ -62,7 +62,7 @@ const HOUR_IN_SECONDS: u32 = 60 * 60;
|
||||
```
|
||||
- 常量可以在任何作用域声明,包括全局作用域;
|
||||
- 常量只能被设置为常量表达式,不能是在运行时计算出来的值。
|
||||
```Rust
|
||||
```rust
|
||||
let a: u32 = 1;
|
||||
let b: u32 = 2;
|
||||
const A: u32 = a + b; //编译错误
|
||||
@@ -72,7 +72,7 @@ const HOUR_IN_SECONDS: u32 = 60 * 60;
|
||||
|
||||
Rust中可以定义一个与之前的变量同名的变量,这称之为第一个变量被第二个变量隐藏。隐藏和`mut`的区别:隐藏是定义了一个新的变量,而使用mut是修改原来的变量。
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn main() {
|
||||
let a: u32 = 1; //这个变量a被下面的a隐藏掉了
|
||||
let a: u32 = 2; //定义了一个新的变量,这个变量也叫作a
|
||||
|
||||
@@ -22,6 +22,7 @@ pub trait GetInformation {
|
||||
### 2.1 为类型实现trait
|
||||
|
||||
代码示例如下:
|
||||
|
||||
```rust
|
||||
// 定义trait
|
||||
pub trait GetInformation {
|
||||
@@ -33,6 +34,7 @@ pub struct Student {
|
||||
pub name: String,
|
||||
pub age: u32,
|
||||
}
|
||||
|
||||
// 为Student类型实现GetInformation trait
|
||||
impl GetInformation for Student {
|
||||
fn get_name(&self) -> &String {
|
||||
@@ -47,6 +49,7 @@ pub struct Teacher {
|
||||
pub name: String,
|
||||
pub age: u32,
|
||||
}
|
||||
|
||||
// 为Teacher类型实现GetInformation trait
|
||||
impl GetInformation for Teacher {
|
||||
fn get_name(&self) -> &String {
|
||||
@@ -77,6 +80,7 @@ fn main() {
|
||||
### 2.2 可以在trait定义时提供默认实现
|
||||
|
||||
可以在定义trait的时候提供默认的行为,trait的类型可以使用默认的行为,示例如下:
|
||||
|
||||
```rust
|
||||
// 定义trait
|
||||
pub trait GetInformation {
|
||||
@@ -90,6 +94,7 @@ pub struct Student {
|
||||
pub name: String,
|
||||
pub age: u32,
|
||||
}
|
||||
|
||||
// 为Student类型实现GetInformation trait
|
||||
impl GetInformation for Student {
|
||||
fn get_name(&self) -> &String {
|
||||
@@ -105,6 +110,7 @@ pub struct Teacher {
|
||||
pub name: String,
|
||||
pub age: u32,
|
||||
}
|
||||
|
||||
// 为Teacher类型实现GetInformation trait
|
||||
impl GetInformation for Teacher {
|
||||
fn get_name(&self) -> &String {
|
||||
@@ -155,6 +161,7 @@ pub struct Student {
|
||||
pub name: String,
|
||||
pub age: u32,
|
||||
}
|
||||
|
||||
// 为Student类型实现GetInformation trait
|
||||
impl GetInformation for Student {
|
||||
fn get_name(&self) -> &String {
|
||||
@@ -169,6 +176,7 @@ pub struct Teacher {
|
||||
pub name: String,
|
||||
pub age: u32,
|
||||
}
|
||||
|
||||
// 为Teacher类型实现GetInformation trait
|
||||
impl GetInformation for Teacher {
|
||||
fn get_name(&self) -> &String {
|
||||
@@ -203,6 +211,7 @@ fn main() {
|
||||
|
||||
|
||||
上面中的print_information函数还可以写成如下:
|
||||
|
||||
```rust
|
||||
// 使用trait bound的写法一
|
||||
pub fn print_information<T: GetInformation>(item: T) {
|
||||
@@ -212,6 +221,7 @@ pub fn print_information<T: GetInformation>(item: T) {
|
||||
```
|
||||
|
||||
这种写法叫做Trait bound语法,它是Rust中用于指定泛型类型参数所需的trait的一种方式,它还可以使用where关键字写成如下:
|
||||
|
||||
```rust
|
||||
// 使用trait bound的写法二
|
||||
pub fn print_information<T>(item: T)
|
||||
@@ -230,6 +240,7 @@ where
|
||||
pub trait GetName {
|
||||
fn get_name(&self) -> &String;
|
||||
}
|
||||
|
||||
pub trait GetAge {
|
||||
fn get_age(&self) -> u32;
|
||||
}
|
||||
@@ -328,6 +339,7 @@ impl GetName for Student {
|
||||
&self.name
|
||||
}
|
||||
}
|
||||
|
||||
struct Teacher {
|
||||
name: String,
|
||||
}
|
||||
@@ -362,7 +374,7 @@ fn main() {
|
||||
错误原因分析(非常重要):
|
||||
上面的代码中的`produce_item_with_name`函数的定义实际上等价于如下:
|
||||
|
||||
```rust
|
||||
```Rust
|
||||
pub fn produce_item_with_name<T: GetName>(is_teacher: bool) -> T {
|
||||
...
|
||||
|
||||
@@ -405,17 +417,19 @@ pub fn produce_item_with_name(is_teacher: bool) -> Student {
|
||||
通过使用带有 `trait bound `的泛型参数的`impl` 块,可以有条件地只为那些实现了特定 trait 的类型实现方法,示例如下:
|
||||
|
||||
```rust
|
||||
|
||||
pub trait GetName {
|
||||
fn get_name(&self) -> &String;
|
||||
}
|
||||
|
||||
pub trait GetAge {
|
||||
fn get_age(&self) -> u32;
|
||||
}
|
||||
|
||||
struct PeopleMatchInformation<T, U> {
|
||||
master: T,
|
||||
employee: U,
|
||||
}
|
||||
|
||||
// 11-15行也可以写成: impl<T: GetName + GetAge, U: GetName + GetAge> PeopleMatchInformation<T, U>
|
||||
impl<T, U> PeopleMatchInformation<T, U>
|
||||
where
|
||||
@@ -429,35 +443,42 @@ where
|
||||
println!("student age = {}", self.employee.get_age());
|
||||
}
|
||||
}
|
||||
|
||||
//使用
|
||||
pub struct Teacher {
|
||||
pub name: String,
|
||||
pub age: u32,
|
||||
}
|
||||
|
||||
impl GetName for Teacher {
|
||||
fn get_name(&self) -> &String {
|
||||
&(self.name)
|
||||
}
|
||||
}
|
||||
|
||||
impl GetAge for Teacher {
|
||||
fn get_age(&self) -> u32 {
|
||||
self.age
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Student {
|
||||
pub name: String,
|
||||
pub age: u32,
|
||||
}
|
||||
|
||||
impl GetName for Student {
|
||||
fn get_name(&self) -> &String {
|
||||
&(self.name)
|
||||
}
|
||||
}
|
||||
|
||||
impl GetAge for Student {
|
||||
fn get_age(&self) -> u32 {
|
||||
self.age
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let t = Teacher {
|
||||
name: String::from("andy"),
|
||||
@@ -486,24 +507,29 @@ fn main() {
|
||||
pub trait GetName {
|
||||
fn get_name(&self) -> &String;
|
||||
}
|
||||
|
||||
pub trait PrintName {
|
||||
fn print_name(&self);
|
||||
}
|
||||
|
||||
// 为实现了GetName trait的类型实现PrintName trait
|
||||
impl<T: GetName> PrintName for T {
|
||||
fn print_name(&self) {
|
||||
println!("name = {}", self.get_name());
|
||||
}
|
||||
}
|
||||
|
||||
// 将为Student实现对应的trait
|
||||
pub struct Student {
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
impl GetName for Student {
|
||||
fn get_name(&self) -> &String {
|
||||
&(self.name)
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let s = Student {
|
||||
name: String::from("Andy"),
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
在Rust中,trait自身不能当作数据类型来使用,但trait 对象可以当作数据类型使用。因此,可以将实现了`Trait A`的类型`B`、`C`、`D`当作`trait A`的trait对象来使用。使用trait对象时,基本都是以引用的方式使用,所以使用时通常是引用符号加`dyn`关键字(即`&dyn`)。
|
||||
|
||||
示例如下:
|
||||
```Rust
|
||||
|
||||
```rust
|
||||
trait GetName {
|
||||
fn get_name(&self);
|
||||
}
|
||||
@@ -77,7 +78,7 @@ fn main() {
|
||||
|
||||
下面通过一段代码来分析一下使用trait对象时内存的布局。代码如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
trait Vehicle {
|
||||
fn run(&self);
|
||||
}
|
||||
@@ -135,7 +136,7 @@ fn main() {
|
||||
|
||||
如下代码编译会报错,因为`Clone`返回的是`Self`:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
pub struct Screen {
|
||||
pub components: Vec<Box<dyn Clone>>,
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ Rust中的生命周期是用来管理内存的一种机制。在Rust中,内存
|
||||
|
||||
生命周期的主要目的就是为了避免悬垂引用。考虑如下代码:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn main() {
|
||||
let r;
|
||||
{
|
||||
@@ -28,7 +28,8 @@ fn main() {
|
||||
Rust编译器有一个借用检查器,用它来检查所有的应用的都是有效的,具体的方式为比较变量及其引用的作用域。
|
||||
|
||||
### 1. 示例1如下:
|
||||
```Rust
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let r; //------------------------+-------'a
|
||||
{ // |
|
||||
@@ -39,12 +40,13 @@ fn main() {
|
||||
println!("r = {}", r); // | //r为悬垂引用
|
||||
} //---------------------------------------+
|
||||
```
|
||||
|
||||
对于上面的代码,借用检查器将`r`的生命周期标注为`'a`,将x的生命周期标注为`'b`,然后比较`'a`和`'b`的范围,发现`'b < 'a`,被引用的对象比它的引用者存在的时间还短,然后编译报错。
|
||||
|
||||
|
||||
### 2. 示例2如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn main() {
|
||||
let x = 5; // ----------+-- 'b
|
||||
// |
|
||||
@@ -54,13 +56,14 @@ fn main() {
|
||||
// --+ |
|
||||
} // ----------+
|
||||
```
|
||||
|
||||
对于上面的代码,借用检查器将x的生命周期标注为`'b`,将`r`的生命周期标注为`'a`,比较两者范围,发现`'b > 'a`,被引用对象比它的应用者存在的时间长,编译检查通过。
|
||||
|
||||
## 3.11.3 编译器有时无法自动推导生命周期
|
||||
|
||||
如下代码会报错:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn longest(x: &str, y: &str) -> &str {
|
||||
if x.len() > y.len() {
|
||||
x
|
||||
@@ -97,7 +100,7 @@ fn main() {
|
||||
|
||||
对于3.11.3例子中的函数,显式标注生命周期后的代码如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
|
||||
if x.len() > y.len() {
|
||||
x
|
||||
@@ -113,7 +116,7 @@ fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
|
||||
|
||||
(1)指定生命周期参数的正确方式依赖函数实现的具体功能。如下代码中将不用标注`y`的生命周期,因为返回值不依赖于y的生命周期。
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn longest<'a>(x: &'a str, y: &str) -> &'a str {
|
||||
x
|
||||
}
|
||||
@@ -121,7 +124,7 @@ fn longest<'a>(x: &'a str, y: &str) -> &'a str {
|
||||
|
||||
(2)当从函数返回一个引用,返回值的生命周期参数需要与一个参数的生命周期参数相匹配。如果返回的引用没有指向任何一个参数,那么唯一的可能就是它指向一个函数内部创建的值,它将会是一个悬垂引用。如下代码将编译错误:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn longest<'a>(x: &str, y: &str) -> &'a str {
|
||||
let result = String::from("really long string");
|
||||
result.as_str() //将产生悬垂引用,result在花括号前“}”离开作用域
|
||||
@@ -132,7 +135,7 @@ fn longest<'a>(x: &str, y: &str) -> &'a str {
|
||||
|
||||
结构体中的生命周期标注示例如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
#[derive(Debug)]
|
||||
struct A<'a> {
|
||||
name: &'a str, // 标注生命周期
|
||||
@@ -149,7 +152,7 @@ fn main() {
|
||||
|
||||
在大多数情况下,程序员不用在代码中显式标注生命周期,因为编译器能自动推导。不标注生命周期,我们称之为生命周期省略。例如下面的代码可以正确编译:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn get_s(s: &str) -> &str {
|
||||
s
|
||||
}
|
||||
@@ -170,6 +173,7 @@ a、每个引用的参数都有它自己的生命周期参数。例如如下:
|
||||
两个引用参数的函数,则有两个生命周期 :`fn foo<'a, 'b>(x: &'a i32, y: &'b i32)`以此类推。
|
||||
|
||||
b、如果只有一个输入生命周期参数,那么它被赋予所有输出生命周期参数:
|
||||
|
||||
```Rust
|
||||
fn foo(x: &i32) -> &i32 等价于 fn foo<'a>(x: &'a i32) -> &'a i32
|
||||
```
|
||||
@@ -180,7 +184,7 @@ c、如果方法有多个输入生命周期参数,不过其中之一因为方
|
||||
|
||||
结构体字段的生命周期必须总是在impl关键字之后声明并在结构体名称之后使用,这些声明周期是结构体类型的一部分,示例如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
struct StuA<'a> {
|
||||
name: &'a str,
|
||||
}
|
||||
@@ -193,7 +197,7 @@ impl<'a> StuA<'a> {
|
||||
|
||||
下面的例子中,其方法没有显式标注生命周期,因为它符合生命周期省略规则中的第三条规则,代码如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
struct StuA<'a> {
|
||||
name: &'a str,
|
||||
}
|
||||
@@ -208,7 +212,7 @@ impl<'a> StuA<'a> {
|
||||
|
||||
静态生命周期定义方式为:`'static`,其生命周期存活于整个程序期间。所有的字符字面值都拥有`'static`生命周期,代码中可以如下来标注:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
let s: &'static str = "I have a static filetime";
|
||||
```
|
||||
|
||||
@@ -216,7 +220,7 @@ let s: &'static str = "I have a static filetime";
|
||||
|
||||
下面示例为在同一函数中指定泛型类型参数、trait bounds 和生命周期:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
use std::fmt::Display;
|
||||
fn longest_with_an_announcement<'a, T>(x: &'a str, y: &'a str, ann: T) -> &'a str
|
||||
where
|
||||
|
||||
@@ -7,7 +7,7 @@ Rust将错误分为两大类:可恢复的和不可恢复的错误。
|
||||
## 3.12.1 用panic!处理不可恢复错误
|
||||
|
||||
`panic!`的使用方式如下:
|
||||
```Rust
|
||||
```rust
|
||||
fn main() {
|
||||
panic!("crash and burn");
|
||||
}
|
||||
@@ -27,7 +27,7 @@ fn main() {
|
||||
|
||||
Rust中使用```Result```类型处理可恢复错误,其定义如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
enum Result<T, E> {
|
||||
Ok(T),
|
||||
Err(E),
|
||||
@@ -40,7 +40,7 @@ enum Result<T, E> {
|
||||
|
||||
使用示例如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
use std::fs::File;
|
||||
fn main() {
|
||||
let f = File::open("hello.txt");
|
||||
@@ -57,7 +57,7 @@ fn main() {
|
||||
|
||||
第3行返回的结果就是一个```Result```类型,可以使用```match```匹配```Result```的具体类型。下面为使用```str```作为```Result<T, E>```中的错误```E```的例子:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
// 该函数返回结果为Result<T, E>,其中T为(),E为具有静态生命周期的&str类型
|
||||
fn produce_error(switch: bool) -> Result<(), &'static str> {
|
||||
if switch {
|
||||
@@ -86,7 +86,7 @@ fn main() {
|
||||
|
||||
#### 3.1 使用unwrap简写:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
use std::fs::File;
|
||||
fn main() {
|
||||
let f = File::open("hello.txt").unwrap(); //使用unwrap简写来获取到Result中的T类型,
|
||||
@@ -96,7 +96,7 @@ fn main() {
|
||||
|
||||
#### 3.2 使用except简写:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
use std::fs::File;
|
||||
|
||||
fn main() {
|
||||
@@ -111,7 +111,7 @@ fn main() {
|
||||
|
||||
除了函数中处理错误外,还可以选择让调用者知道这个错误并决定如何处理,这叫做传播错误。示例如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn produce_error(switch: bool) -> Result<(), &'static str> {
|
||||
if switch {
|
||||
return Err("This is a error");
|
||||
@@ -145,7 +145,7 @@ fn main() {
|
||||
|
||||
传播错误可以用```?```进行简写,上面的```transmit_error```函数代码用简写方式示例如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn produce_error(switch: bool) -> Result<(), &'static str> {
|
||||
if switch {
|
||||
return Err("This is a error");
|
||||
@@ -175,7 +175,7 @@ fn main() {
|
||||
|
||||
下面是更复杂的简写:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
use std::io;
|
||||
use std::io::Read;
|
||||
use std::fs::File;
|
||||
|
||||
@@ -8,7 +8,6 @@ Rust 中的 HashMap(哈希映射)是一个基于键值对的无序集合,
|
||||
要创建一个新的空 HashMap,可以使用 `HashMap::new()` 方法。需要导入 `std::collections::HashMap` 模块以使用 HashMap。
|
||||
|
||||
```rust
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
let mut map = HashMap::new();
|
||||
@@ -17,6 +16,7 @@ let mut map = HashMap::new();
|
||||
## 2. 插入键值对:
|
||||
|
||||
可以使用 `insert()` 方法向 HashMap 中添加键值对。如果使用相同的键插入新值,旧值将被替换。
|
||||
|
||||
```rust
|
||||
map.insert("one", 1);
|
||||
map.insert("two", 2);
|
||||
@@ -51,6 +51,7 @@ map.remove("one"); // 删除键为 "one" 的键值对
|
||||
## 6. 检查键是否存在:
|
||||
|
||||
可以使用 `contains_key()` 方法检查 HashMap 中是否存在指定的键。
|
||||
|
||||
```rust
|
||||
let has_key = map.contains_key("one"); // 返回布尔值
|
||||
```
|
||||
@@ -58,6 +59,7 @@ let has_key = map.contains_key("one"); // 返回布尔值
|
||||
## 7. 更新值:
|
||||
|
||||
可以使用 `entry()` 方法与 `or_insert()` 方法结合,更新 HashMap 中的值或插入新值。
|
||||
|
||||
```rust
|
||||
*map.entry("three").or_insert(3) += 1;
|
||||
```
|
||||
|
||||
@@ -6,6 +6,7 @@ Rust 中的 LinkedList(链表)是一种线性数据结构,它由一系列
|
||||
## 1. 创建 LinkedList:
|
||||
|
||||
要创建一个新的空 LinkedList,可以使用 `LinkedList::new()` 方法。需要导入 `std::collections::LinkedList` 模块以使用 LinkedList。
|
||||
|
||||
```rust
|
||||
use std::collections::LinkedList;
|
||||
|
||||
@@ -15,6 +16,7 @@ let mut list = LinkedList::new();
|
||||
## 2. 添加元素:
|
||||
|
||||
可以使用 `push_front()` 和 `push_back()` 方法将元素添加到链表的开头和结尾。
|
||||
|
||||
```rust
|
||||
list.push_front(1);
|
||||
list.push_back(2);
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
## 1. Box的基本使用方式
|
||||
|
||||
下面为```Box```使用的简单示例:
|
||||
```Rust
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let b = Box::new(5); //此时5存储在堆上而不是栈上,b本身存储于栈上
|
||||
println!("b = {}", b); //离开作用域时同时清楚堆和栈上的数据
|
||||
@@ -27,7 +28,8 @@ fn main() {
|
||||
|
||||
### (1)场景1示例:
|
||||
假定我们需要采用递归的方式定义一个```List```,其定义可能如下:
|
||||
```Rust
|
||||
|
||||
```rust
|
||||
// 下面的代码无法编译通过
|
||||
use crate::List::{Nil, Cons};
|
||||
enum List {
|
||||
@@ -48,7 +50,8 @@ fn main() {
|
||||

|
||||
|
||||
此时就需要使用Box,其代码如下:
|
||||
```Rust
|
||||
|
||||
```rust
|
||||
use crate::List::{Nil, Cons};
|
||||
enum List {
|
||||
Cons(i32, Box<List>), // 用Box就把它变成了一个指针,Cons就类似于c语言的结构体定义:
|
||||
@@ -70,7 +73,8 @@ fn main() {
|
||||
每个Box的大小是固定的,所以编译不会有问题。
|
||||
|
||||
### (2)场景2示例:
|
||||
```Rust
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let b = Box::new([100u32; 100]);
|
||||
println!("b = {:?}", b);
|
||||
@@ -82,7 +86,8 @@ fn main() {
|
||||
```
|
||||
|
||||
### (3)场景3示例:
|
||||
```Rust
|
||||
|
||||
```rust
|
||||
trait Vehicle {
|
||||
fn run(&self);
|
||||
}
|
||||
@@ -118,4 +123,3 @@ fn main() {
|
||||
vehicle_run(v2);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
常规引用是一个指针类型,包含了目标数据存储的内存地址。对常规引用使用 `*` ,可以通过解引用的方式获取到内存地址对应的数据值,示例如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn main() {
|
||||
let x = 5;
|
||||
let y = &x;
|
||||
@@ -18,7 +18,8 @@ fn main() {
|
||||
## 2. 通过*使用智能指针背后的值
|
||||
|
||||
对于智能指针,也可以通过`*`使用其背后的值,示例如下:
|
||||
```Rust
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let x = 5;
|
||||
let y = Box::new(x);
|
||||
@@ -31,15 +32,15 @@ fn main() {
|
||||
|
||||
实现`Deref trait`允许我们重载解引用运算符`*`。通过为类型实现`Deref trait`,类型可以被当做常规引用来对待。简单来说,如果类型A实现了`Deref trait`,那么就可以写如下代码:
|
||||
|
||||
```Rust
|
||||
let a: A = A::new();
|
||||
let b = &a;
|
||||
let c = *b; //对A实现了Deref trait,所以可以对A类型解引用
|
||||
```rust
|
||||
let a: A = A::new();
|
||||
let b = &a;
|
||||
let c = *b; //对A实现了Deref trait,所以可以对A类型解引用
|
||||
```
|
||||
|
||||
下面的代码定义一个`MyBox`类型,并为其实现`Deref trait`:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
use std::ops::Deref;
|
||||
struct MyBox<T>(T);
|
||||
impl<T> MyBox<T> {
|
||||
@@ -67,7 +68,7 @@ fn main() {
|
||||
|
||||
对于函数和方法的传参,Rust 提供了隐式转换:Deref 转换。若一个类型实现了 Deref 特征,那它的引用在传给函数或方法时,会根据参数签名来决定是否进行隐式的 Deref 转换,例如:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
use std::ops::Deref;
|
||||
struct MyString(String);
|
||||
impl Deref for MyString { // MyString类型实现了Deref trait
|
||||
@@ -95,7 +96,7 @@ fn main() {
|
||||
|
||||
下面为调用方法时发生的隐式自动转换的例子:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
use std::ops::Deref;
|
||||
struct MyType(u32);
|
||||
impl MyType {
|
||||
@@ -125,7 +126,7 @@ fn main() {
|
||||
|
||||
Deref还支持连续的隐式转换,示例如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn print(s: &str) {
|
||||
println!("{}", s);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
为一个类型实现`Drop trait`的示例如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
struct Dog(String);
|
||||
|
||||
//下面为Dog实现Drop trait
|
||||
@@ -32,7 +32,7 @@ fn main() {
|
||||
|
||||
当要显示的清理值时,不能直接调用Drop trait里面的drop方法,而要使用`std::mem::drop`方法,示例如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
struct Dog(String);
|
||||
|
||||
//下面为Dog实现Drop trait
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
根据前面的知识,可能写出来的代码如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
enum List {
|
||||
Cons(i32, Box<List>),
|
||||
Nil,
|
||||
@@ -27,7 +27,7 @@ fn main() {
|
||||
|
||||
Rc智能指针通过引用计数解决数据共享的问题,下面是Rc使用的简单代码:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
use std::rc::Rc;
|
||||
fn main() {
|
||||
let a = Rc::new(5u32);
|
||||
@@ -42,7 +42,8 @@ fn main() {
|
||||

|
||||
|
||||
前面共享列表的需求则可以使用Rc实现如下:
|
||||
```Rust
|
||||
|
||||
```rust
|
||||
enum List {
|
||||
Cons(i32, Rc<List>),
|
||||
Nil,
|
||||
@@ -66,7 +67,7 @@ fn main() {
|
||||
|
||||
下面的示例打印了Rc的引用计数:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
enum List {
|
||||
Cons(i32, Rc<List>),
|
||||
Nil,
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
**内部可变性(Interior mutability)** 是Rust中的一个设计模式,它允许在有不可变引用时改变数据,这通常是借用规则所不允许的。RefCell正是为Rust提供内部可变性的智能指针。
|
||||
在Rust中,当使用mut或者&mut显示的声明一个变量或者引用时,才能修改它们的值。编译器会对此严格检查。
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
let mut a = 1u32;
|
||||
a = 2u32; // 可以修改a的值
|
||||
let b = 3u32;
|
||||
@@ -14,7 +14,7 @@ b = 4u32; // 报错,不允许修改
|
||||
|
||||
但是当使用RefCell时,可以对其内部包含的内容进行修改,如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
use std::cell::RefCell;
|
||||
fn main() {
|
||||
let data = RefCell::new(1); // data本身是不可变变量
|
||||
@@ -32,7 +32,7 @@ fn main() {
|
||||
|
||||
下面是另一个使用RefCell的例子:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
#[derive(Debug)]
|
||||
enum List {
|
||||
Cons(Rc<RefCell<i32>>, Rc<List>),
|
||||
@@ -61,7 +61,7 @@ fn main() {
|
||||
|
||||
下面是一个使用RefCell时容易犯错的例子:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
use std::cell::RefCell;
|
||||
fn main() {
|
||||
let data = RefCell::new(1); // data本身是不可变变量
|
||||
@@ -75,7 +75,7 @@ fn main() {
|
||||
> ***分析:此处需要注意的是,对于RefCell的可变引用、不可变引用的作用域范围(Rust 1.68.2中),其定义方式还是从定义开始,到花括号前结束。这和普通引用是不一样的,因为在新版编译器中(1.31以后),普通引用的作用域范围变成了从定义开始,到不再使用结束。因此,下面的代码是可以正确的***:
|
||||
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn main() {
|
||||
let mut a = 5u32;
|
||||
let b = &mut a; // b是可变引用
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
下面的代码试图用之前学到的智能指针相关知识实现上面的链表:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
use crate::List::{Cons, Nil};
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
@@ -91,7 +91,7 @@ Weak类似于Rc,但它不持有所有权,它仅仅保存一份指向数据
|
||||
|
||||
对于上一节中循环链表的例子,使用Weak实现为如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
use crate::List::{Cons, Nil};
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
# 3.17.2 模块 (Module)
|
||||
## 1. 使用模块对代码分组
|
||||
使用模块方便对代码进行分组,以提高可读性和重用性,例如如下代码:
|
||||
```Rust
|
||||
```rust
|
||||
fn main() {
|
||||
println!("Produce something!");
|
||||
}
|
||||
```
|
||||
将其中的打印代码放入到一个模块中,变成如下:
|
||||
```Rust
|
||||
```rust
|
||||
mod factory { // 创建一个模块
|
||||
pub fn produce() { // 将打印函数放在模块中
|
||||
println!("Produce something!");
|
||||
@@ -21,7 +21,7 @@ fn main() {
|
||||
|
||||
## 2. 定义模块控制作用域和私有性
|
||||
使用模块可以控制作用域和私有性,示例如下:
|
||||
```Rust
|
||||
```rust
|
||||
mod factory {
|
||||
pub struct PubStruct {
|
||||
// 该结构体被定义为公有,外部可以使用
|
||||
@@ -32,9 +32,9 @@ mod factory {
|
||||
// 该结构体定义为私有,外部无法使用
|
||||
i: u32,
|
||||
}
|
||||
|
||||
|
||||
// 该函数被定义为公有,外部可以使用
|
||||
pub fn function1() {
|
||||
pub fn function1() {
|
||||
let p1 = PubStruct { i: 3u32 };
|
||||
println!("p1 = {:?}", p1.i);
|
||||
let p2 = PrivateStruct { i: 3u32 };
|
||||
@@ -70,7 +70,7 @@ fn main() {
|
||||
- 绝对路径(absolute path):以 crate root开头的全路径;对于外部 crate 的代码,是以 crate 名开头的绝对路径,对于对于当前 crate 的代码,则以字面值 crate 开头。
|
||||
- 相对路径(relative path):从当前模块开始,以 self、super 或当前模块的标识符开头。
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
mod parent {
|
||||
pub struct A(pub u32);
|
||||
|
||||
@@ -92,7 +92,7 @@ fn main() {
|
||||
|
||||
## 4. 使用use关键字引入作用域
|
||||
在外部使用模块中的每个项都带上路径会显得比较重复,可以使用use关键字引入路径,示例如下:
|
||||
```Rust
|
||||
```rust
|
||||
mod parent {
|
||||
pub struct A(pub u32);
|
||||
|
||||
@@ -123,7 +123,7 @@ fn main() {
|
||||
```
|
||||
|
||||
还可以使用as关键字为引入的项提供新的名字,示例如下:
|
||||
```Rust
|
||||
```rust
|
||||
pub mod factory {
|
||||
pub fn produce() {
|
||||
println!("Produce something!");
|
||||
@@ -140,7 +140,7 @@ fn main() {
|
||||
我们将上面第4点中的第二个例子拆成多个文件,步骤如下:
|
||||
|
||||
- 创建一个factory.rs,其内容为mod factory中的内容:
|
||||
```Rust
|
||||
```rust
|
||||
// src/factory.rs
|
||||
pub fn produce() {
|
||||
println!("Produce something!");
|
||||
@@ -148,7 +148,7 @@ pub fn produce() {
|
||||
```
|
||||
|
||||
- 在main.rs中导出mod,如下:
|
||||
```Rust
|
||||
```rust
|
||||
// src/main.rs
|
||||
mod factory; // 导出factory module,module名字和文件名字同名
|
||||
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
# 3.17.3 再谈crate
|
||||
## 1. 创建二进制crate和库crate
|
||||
可以被编译成可执行文件的crate就是二进制crate,它的代码中必定包含一个main函数。如下方式创建的就是一个二进制crate:
|
||||
```
|
||||
|
||||
```bash
|
||||
cargo new main
|
||||
```
|
||||
|
||||
对于库crate的创建,需要加上--lib方式如下:
|
||||
```
|
||||
|
||||
```bash
|
||||
cargo new my-lib --lib
|
||||
```
|
||||
|
||||
@@ -17,12 +19,14 @@ cargo new my-lib --lib
|
||||
|
||||
下面以使用第三方crate rand为例,来进行演示。
|
||||
### (1)首先创建一个工程:
|
||||
```
|
||||
|
||||
```bash
|
||||
cargo new main
|
||||
```
|
||||
|
||||
### (2)添加依赖:
|
||||
```
|
||||
|
||||
```bash
|
||||
cd main
|
||||
```
|
||||
|
||||
@@ -33,13 +37,13 @@ cd main
|
||||
### (3)在代码中使用rand库
|
||||
编写src/main.rs如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
// src/main.rs
|
||||
use rand::prelude::*; // 引入rand库
|
||||
|
||||
fn main() {
|
||||
// 使用rand的函数
|
||||
let mut rng = rand::thread_rng();
|
||||
let mut rng = rand::thread_rng();
|
||||
let y: f64 = rng.gen();
|
||||
println!("y = {:?}", y);
|
||||
}
|
||||
|
||||
@@ -1,23 +1,27 @@
|
||||
# 3.17.4 工作空间
|
||||
|
||||
**工作空间** 是一系列共享同样的 Cargo.lock 和输出目录的包。使用工作空间,可以将多个crate放在同一个目录下,通过共享依赖来提供更好的代码组织和构建支持。
|
||||
假定有一个项目my-project,里面包含两个crate,分别是二进制crate main和库crate add,在crate main的代码中使用crate add的功能。下面的示例通过工作空间来组织和管理crate。
|
||||
|
||||
## 1.创建整个工程
|
||||
命令如下:
|
||||
```
|
||||
|
||||
```bash
|
||||
mkdir my-project
|
||||
cd my-project
|
||||
```
|
||||
|
||||
## 2.创建crate add
|
||||
命令如下:
|
||||
```
|
||||
|
||||
```bash
|
||||
cargo new add --lib
|
||||
```
|
||||
|
||||
## 3.编写crate adder的代码
|
||||
编辑add/src/lib.rs如下:
|
||||
```Rust
|
||||
|
||||
```rust
|
||||
// add/src/lib.rs
|
||||
pub fn add(left: u32, right: u32) -> u32 {
|
||||
left + right
|
||||
@@ -26,7 +30,8 @@ pub fn add(left: u32, right: u32) -> u32 {
|
||||
|
||||
## 4.创建crate main
|
||||
命令如下:
|
||||
```
|
||||
|
||||
```bash
|
||||
cargo new main
|
||||
```
|
||||
|
||||
@@ -36,7 +41,7 @@ cargo new main
|
||||
# my-project/Cargo.toml
|
||||
[workspace]
|
||||
members = [
|
||||
"./main",
|
||||
"./main",
|
||||
"./add",
|
||||
]
|
||||
```
|
||||
@@ -57,7 +62,8 @@ add = { path = "../add" } # 添加这行:添加对crate add的依赖
|
||||
## 7.在crate main的代码中使用crate add的代码
|
||||
|
||||
编辑main/src/lib.rs如下:
|
||||
```Rust
|
||||
|
||||
```rust
|
||||
// main/src/lib.rs
|
||||
use add::*;
|
||||
fn main() {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# 3.18.1 编写测试
|
||||
|
||||
## 1. 测试初体验
|
||||
|
||||
测试函数体通常执行如下三种操作:
|
||||
- 设置需要的数据或者状态;
|
||||
- 运行需要测试的代码;
|
||||
@@ -9,14 +11,16 @@ Rust提供了专门用来编写测试的功能,即test属性、一些宏和sho
|
||||
Rust中的测试就是带有一个test属性注解的函数,使用Cargo创建一个新的库时,会自动生成一个测试模块和测试函数,这个模块就是一个编写测试的模板。下面进行演示:
|
||||
### (1)创建一个库
|
||||
运行如下命令生成一个库:
|
||||
```
|
||||
|
||||
```bash
|
||||
cargo new adder --lib
|
||||
```
|
||||
|
||||
### (2)查看自动生成的测试模块
|
||||
打开adder/src/lib.rs可以看到其中内容如下:
|
||||
```Rust
|
||||
pub fn add(left: usize, right: usize) -> usize {
|
||||
|
||||
```rust
|
||||
pub fn add(left: usize, right: usize) -> usize {
|
||||
left + right
|
||||
}
|
||||
|
||||
@@ -25,7 +29,7 @@ pub fn add(left: usize, right: usize) -> usize {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
#[test]
|
||||
fn it_works() { // 这是一个测试函数,上面需要加上#[test]
|
||||
let result = add(2, 2); // 调用被测试的函数
|
||||
assert_eq!(result, 4); // 用断言进行判断结果
|
||||
@@ -36,7 +40,8 @@ mod tests {
|
||||
|
||||
### (3)运行测试
|
||||
在adder目录下运行如下命令来执行测试函数:
|
||||
```
|
||||
|
||||
```bash
|
||||
cargo test
|
||||
```
|
||||
|
||||
@@ -50,7 +55,8 @@ cargo test
|
||||
- assert_ne!输入两个参数,两个参数不等,断言成功,否则断言失败;
|
||||
|
||||
几个断言的使用示例如下:
|
||||
```Rust
|
||||
|
||||
```rust
|
||||
pub fn add(left: usize, right: usize) -> usize {
|
||||
left + right
|
||||
}
|
||||
@@ -63,7 +69,7 @@ mod tests {
|
||||
fn it_works() {
|
||||
let result = add(2, 2);
|
||||
assert!(result==4); // 断言成功
|
||||
assert!(result==2); // 断言将失败
|
||||
assert!(result==2); // 断言将失败
|
||||
assert_eq!(result, 4); // 断言成功
|
||||
assert_eq!(result, 2); // 断言失败
|
||||
assert_ne!(result, 2); // 断言成功
|
||||
@@ -74,7 +80,8 @@ mod tests {
|
||||
|
||||
## 3. 使用should_panic!
|
||||
可以使用should_panic!检查代码是否按照预期panic,示例如下:
|
||||
```Rust
|
||||
|
||||
```rust
|
||||
pub struct Guess {
|
||||
value: i32,
|
||||
}
|
||||
@@ -103,7 +110,8 @@ mod tests {
|
||||
|
||||
## 4. 使用Result
|
||||
除了使用断言,还是可以使用Result类型。和断言不同的是,断言写在函数中,而Result类型则是作为测试函数的返回值,示例如下:
|
||||
```Rust
|
||||
|
||||
```rust
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
|
||||
@@ -5,14 +5,16 @@
|
||||
## 1. 并行或连续运行测试
|
||||
|
||||
当运行多个测试时,Rust默认使用线程并行运行。如果不希望测试并行运行,或者更加精确的控制线程的数量,可以传递 --test-threads参数来控制,示例如下:
|
||||
```
|
||||
|
||||
```bash
|
||||
cargo test -- --test-threads=1 # 使用一个线程运行测试
|
||||
```
|
||||
|
||||
## 2. 显示测试中的打印
|
||||
|
||||
有的时候,需要在运行测试时打印内容到标准输出,可以添加-- --nocapture或者-- --show-output,例如有如下代码:
|
||||
```Rust
|
||||
|
||||
```rust
|
||||
pub fn add(left: usize, right: usize) -> usize {
|
||||
left + right
|
||||
}
|
||||
@@ -30,18 +32,20 @@ mod tests {
|
||||
}
|
||||
```
|
||||
要在运行测试时显示12行的打印,可以运行如下命令:
|
||||
```
|
||||
|
||||
```bash
|
||||
cargo test -- --nocapture
|
||||
```
|
||||
或者
|
||||
```
|
||||
```bash
|
||||
cargo test -- --show-output
|
||||
```
|
||||
|
||||
## 3. 运行单个测试
|
||||
|
||||
运行测试时,可以通过指定测试函数的名字来运行某个特定的测试函数。例如有如下测试代码:
|
||||
```Rust
|
||||
|
||||
```rust
|
||||
pub fn add_two(a: i32) -> i32 {
|
||||
a + 2
|
||||
}
|
||||
@@ -66,8 +70,10 @@ mod tests {
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
通过指定函数名运行特定的测试函数,下面的命令只会运行函数add_two_and_two:
|
||||
```
|
||||
|
||||
```bash
|
||||
cargo test add_two_and_two
|
||||
```
|
||||
|
||||
@@ -78,7 +84,8 @@ cargo test add_two_and_two
|
||||
## 4. 过滤运行测试
|
||||
|
||||
还可以指定部分测试的名称,任何名称匹配这个名称的测试会被运行。例如,因为上面的代码中,前两个测试的名称包含 add,则可以通过 cargo test add 来运行这两个测试:
|
||||
```
|
||||
|
||||
```bash
|
||||
cargo test add
|
||||
```
|
||||
|
||||
@@ -87,8 +94,10 @@ cargo test add
|
||||

|
||||
|
||||
## 5. 忽略某个测试
|
||||
|
||||
有时候运行cargo test时想忽略其中的某个测试,此时可以通过使用ignore属性来标记该测试来排除它。例如有如下测试代码:
|
||||
```Rust
|
||||
|
||||
```rust
|
||||
pub fn add_two(a: i32) -> i32 {
|
||||
a + 2
|
||||
}
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
## 3.19.1 包和模块级别的注释
|
||||
包和模块的注释分为两种:行注释 //! 和块注释 /*! ... */ ,这些注释需要添加到文件的最上方。示例如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
/*!
|
||||
这是一个crate,里面有mod add,此处用的是块注释
|
||||
这是一个crate,里面有mod add,此处用的是块注释
|
||||
*/
|
||||
//! 此处再使用一下行注释
|
||||
//! 此处再使用一下行注释
|
||||
mod add;
|
||||
pub mod sub;
|
||||
```
|
||||
@@ -16,9 +16,10 @@ pub mod sub;
|
||||

|
||||
|
||||
而下面的示例因为没有将所有的包注释放在文件最上面,所以会报错:
|
||||
```Rust
|
||||
|
||||
```rust
|
||||
/*!
|
||||
这是一个crate,里面有mod add,此处用的是块注释
|
||||
这是一个crate,里面有mod add,此处用的是块注释
|
||||
*/
|
||||
pub mod add;
|
||||
|
||||
@@ -28,9 +29,12 @@ pub mod sub;
|
||||
|
||||
|
||||
## 3.19.2 文档测试
|
||||
|
||||
### 1. 文档测试
|
||||
|
||||
Rust运行在文档注释中写测试用例,示例如下:
|
||||
```Rust
|
||||
|
||||
```rust
|
||||
/// `add` 将两个值相加
|
||||
///
|
||||
/// 下面是测试用例
|
||||
@@ -47,8 +51,10 @@ pub fn add(left: u32, right: u32) -> u32 {
|
||||
测试用例的内容用一对```包含,上面的代码在运行cargo test时,将会运行注释中的测试用例。
|
||||
|
||||
### 2. 保留测试,隐藏注释
|
||||
|
||||
还可以保留文档测试的功能,但是把测试用例的内容在文档中隐藏起来,示例如下:
|
||||
```Rust
|
||||
|
||||
```rust
|
||||
/// `add` 将两个值相加
|
||||
///
|
||||
/// 下面是测试用例
|
||||
@@ -105,7 +111,8 @@ pub fn sub(left: u32, right: u32) -> Option<u32> {
|
||||
|
||||
### 2. 文档搜索别名
|
||||
可以在Rust文档中为类型定义搜索别名,以便更好的进行搜索,示例如下:
|
||||
```Rust
|
||||
|
||||
```rust
|
||||
#[doc(alias("x"))]
|
||||
pub struct A;
|
||||
```
|
||||
|
||||
@@ -26,7 +26,7 @@ Rust中的整型和浮点型如下:
|
||||
|
||||
(1)可以在数值字面量后面加上类型表示该类型的数值,如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn main(){
|
||||
let _a = 33u32; // 直接加类型后缀
|
||||
let _b = 33_i32; // 使用_分隔数值和类型
|
||||
@@ -37,7 +37,7 @@ fn main(){
|
||||
|
||||
(2)可以数值的任意位置使用下划线分割来增强可读性,如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn main(){
|
||||
let _a = 10_000_000u32;
|
||||
let _b = 1_234_3_u32;
|
||||
@@ -47,7 +47,7 @@ fn main(){
|
||||
|
||||
(3)当不明确指定变量的类型,也不明确指定数值字面量的类型后缀时,Rust默认将整数当做`i32`类型,将浮点数当做`f64`类型,如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn main(){
|
||||
let _a = 33; // 等价于 let _a: i32 = 33; 等价于 let _a = 33i32;
|
||||
let _b = 64.123; // 等价于let _b: f64 = 64.123; 等价于 let _b = 64.123f64;
|
||||
@@ -56,7 +56,7 @@ fn main(){
|
||||
|
||||
(4)Rust使用`0b`表示二进制、`0o`表示八进制、`0x`表示十六进制,如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn main(){
|
||||
let a: u32 = 0b101; // 二进制整数
|
||||
let b: i32 = 0o17; // 八进制整数
|
||||
@@ -68,7 +68,7 @@ fn main(){
|
||||
|
||||
(5)Rust中所有的数值类型都支持基本数学运算:加、减、乘、除、取余,如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn main() {
|
||||
let sum = 5 + 10;
|
||||
let difference = 95.5 - 4.3;
|
||||
@@ -83,7 +83,7 @@ fn main() {
|
||||
|
||||
Rust中的布尔型用`bool`表示,有两个可能的值,为`true`和`false`。布尔类型使用的场景主要是条件表达式(控制流的内容),使用如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn main() {
|
||||
// 定义方式
|
||||
let a: bool = true;
|
||||
@@ -108,7 +108,7 @@ fn main() {
|
||||
|
||||
char类型用于存放单个unicode字符,占用4个字节空间。当存储char类型数据时,Rust会将其转换为utf-8编码的数据存储。`char`字面量是单引号包含的任意单个字符,字符类型使用示例如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn main() {
|
||||
let c: char = 'z';
|
||||
let x: char = 'x';
|
||||
@@ -124,7 +124,7 @@ fn main() {
|
||||
|
||||
圆括号以及其中逗号分割的值列表组成元组,定义一个元组方式如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn main() {
|
||||
let tup: (i32, f64, u8) = (500, 6.4, 1);
|
||||
}
|
||||
@@ -132,7 +132,7 @@ fn main() {
|
||||
|
||||
可以将元组重新结构到变量上,如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn main() {
|
||||
let tup = (500, 6.4, 1);
|
||||
let (x, y, z) = tup;
|
||||
@@ -142,7 +142,7 @@ fn main() {
|
||||
|
||||
也可以直接使用元组的元素,如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn main() {
|
||||
let x: (i32, f64, u8) = (500, 6.4, 1);
|
||||
let first = x.0;
|
||||
@@ -153,7 +153,7 @@ fn main() {
|
||||
|
||||
不带任何值的元组,称为unit类型(单元元组),可以代表空值或者空的返回类型,如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn main() {
|
||||
let x: () = (); // 将值()保存到类型为()的变量x中
|
||||
}
|
||||
@@ -163,7 +163,7 @@ fn main() {
|
||||
|
||||
数组中的每个元素的类型必须相同,数组的长度是固定的,数组的定义方式如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn main() {
|
||||
let a = [1, 2, 3, 4, 5]; //直接写数组的值
|
||||
let b: [i32; 5] = [1, 2, 3, 4, 5]; //显示指定数组的类型和长度
|
||||
@@ -173,7 +173,7 @@ fn main() {
|
||||
|
||||
数组通过索引来访问元素,索引从0开始计数,如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn main() {
|
||||
let a = [1, 2, 3, 4, 5];
|
||||
|
||||
@@ -184,7 +184,7 @@ fn main() {
|
||||
|
||||
Rust中,访问无效的索引元素会报错,如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn main() {
|
||||
let a = [1, 2, 3, 4, 5];
|
||||
let b = a[5]; // 错误,只能放为0-4,所以这个代码将无法编译
|
||||
@@ -202,7 +202,7 @@ Rust中可以使用`as`进行类型转换。
|
||||
- 可以使用`std::char::from_u32`将`u32`转换为`char`类型。
|
||||
- 可以使用`std::char::from_digit`将十进制整型转换为`char`类型。
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn main() {
|
||||
// 数值类型的转换
|
||||
assert_eq!(10i8 as u16, 10u16);
|
||||
|
||||
@@ -62,6 +62,7 @@ fn main() {
|
||||
### 3.20.1.4. 线程与move闭包
|
||||
|
||||
move关键字可用于传递给thread::spawn的闭包,获取环境中的值的所有权,从而达到将值的所有权从一个线程传送到另一个线程的目的。示例如下:
|
||||
|
||||
```rust
|
||||
use std::thread;
|
||||
fn main() {
|
||||
@@ -82,6 +83,7 @@ Rust中实现消息传递并发的主要工具是通道。通道由发送者和
|
||||
- 接收者用来接收消息;
|
||||
- 发送者或者接收者任一被丢弃时就认为通道被关闭了。
|
||||
Rust标准库中提供的通道叫做mpsc,是多个生产者,单个消费者的通道,其使用示例如下:
|
||||
|
||||
```rust
|
||||
use std::sync::mpsc;
|
||||
use std::thread;
|
||||
@@ -107,6 +109,7 @@ fn main() {
|
||||
### 3.20.2.2. 通道和所有权
|
||||
|
||||
在使用通道时,send 函数会获取参数的所有权并移动这个值归接收者所有。例如下面的代码将会编译错误:
|
||||
|
||||
```rust
|
||||
use std::sync::mpsc;
|
||||
use std::thread;
|
||||
@@ -251,6 +254,7 @@ fn main() {
|
||||
```
|
||||
|
||||
因此要在多个线程间共享内存,还需要有一种机制能让多个线程都能访问Mutex保护的数据。根据本书目前学到的知识,可以想到Rc智能指针。上面的代码使用Rc智能指针后的示例如下:
|
||||
|
||||
```rust
|
||||
use std::rc::Rc;
|
||||
use std::sync::Mutex;
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
|
||||
build.rs中可以进行真正的项目代码编译前需要的额外的工作,例如在编译前为项目生成对应的文件、代码,编译所依赖的外部语言库等。build.rs放置在正式代码的外面(也就是src的外面)。
|
||||
下面示例在build.rs中生成一个文件,然后在正式的项目代码中读取这个文件,build.rs中的代码如下:
|
||||
```Rust
|
||||
|
||||
```rust
|
||||
// build.rs
|
||||
use std::fs;
|
||||
|
||||
@@ -16,7 +17,8 @@ fn main() -> std::io::Result<()> {
|
||||
```
|
||||
|
||||
src/main.rs中的代码如下:
|
||||
```Rust
|
||||
|
||||
```rust
|
||||
// src/main.rs
|
||||
use std::fs;
|
||||
fn main() {
|
||||
@@ -43,10 +45,11 @@ fn main() {
|
||||
在项目构建之前,Cargo会将build.rs编译成可执行文件,然后执行。在执行过程中,脚本可以使用println的方式跟Cargo进行通信,通信内容的格式为:cargo:真正的内容。
|
||||
|
||||
示例如下:
|
||||
```Rust
|
||||
|
||||
```rust
|
||||
// build.rs
|
||||
fn main() {
|
||||
println!("cargo:rustc-link-search=/usr/local/lib/");
|
||||
println!("cargo:rustc-link-search=/usr/local/lib/");
|
||||
println!("cargo:rustc-link-lib=dylib=pcre2-8");
|
||||
println!("cargo:rerun-if-changed=src/lib.rs");
|
||||
}
|
||||
@@ -68,6 +71,7 @@ fn main() {
|
||||
- 构建脚本的依赖
|
||||
|
||||
构建脚本也可以引入其它基于Cargo的依赖包,依赖方式为在Cargo.toml中添加依赖包,示例如下:
|
||||
|
||||
```TOML
|
||||
# Cargo.toml
|
||||
|
||||
@@ -87,13 +91,17 @@ cc = "1.0.46" # 可以在build.rs中使用cc相关的功能
|
||||

|
||||
|
||||
c目录中的pass.c为c代码,源码如下:
|
||||
```Rust
|
||||
|
||||
```C
|
||||
// c/pass.c
|
||||
#include <stdio.h>
|
||||
|
||||
void set_err(char *message) { // 提供一个打印错误信息的函数
|
||||
printf("err: %s\n", message);
|
||||
}
|
||||
```
|
||||
|
||||
```Rust
|
||||
src目录中为Rust代码,源码如下:
|
||||
// ==================
|
||||
// 封装c函数
|
||||
@@ -114,7 +122,7 @@ fn main() {
|
||||
|
||||
build.rs中的内容如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
// build.rs
|
||||
fn main() {
|
||||
// 以下代码告诉 Cargo ,`c/pass.c`发生改变,就重新运行当前的构建脚本
|
||||
@@ -144,5 +152,3 @@ libc = "0.2.139" # src/main.rs中使用了libc这个库
|
||||
```
|
||||
|
||||
可以使用cargo run对这个工程编译运行。
|
||||
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
目录中的foo是Rust代码,用cargo new foo --lib创建,其中src/lib.rs的代码如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
#![crate_type = "staticlib"] // 生成静态库
|
||||
#[no_mangle] // 暴露给c的函数要声明#[no_mangle]
|
||||
pub extern fn foo(){
|
||||
@@ -40,7 +40,7 @@ crate-type = ["staticlib"] # 表示编译为静态库
|
||||
在foo目录下运行命令cargo build编译,在foo/target/debug/目录下会生成静态库libfoo.a。
|
||||
|
||||
main.c中的c代码如下:
|
||||
```Rust
|
||||
```C
|
||||
#include <stdio.h>
|
||||
|
||||
extern void foo(); // foo函数为Rust提供的函数
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
# 3.23.1 声明宏
|
||||
声明宏使用macro_rules!定义,是Rust中最常用的宏形式。下面代码中定义Vec时使用的vec!就是一个声明宏:
|
||||
```Rust
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let _v = vec![1, 2, 3]; // 使用声明宏vec!定义一个Vec
|
||||
}
|
||||
```
|
||||
|
||||
下面的例子演示定义一个声明宏并使用它:
|
||||
```Rust
|
||||
|
||||
```rust
|
||||
// 定义一个声明宏my_vec
|
||||
macro_rules! my_vec {
|
||||
( $( $x:expr ),* ) => {
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
|
||||
## 1. 自定义derive宏
|
||||
在本书之前就已经见到过的结构体上的#[derive(Copy, Debug, Clone)]就是自定义derive宏,其功能实际上就是为类型生成对应的代码。例如下面的代码:
|
||||
```Rust
|
||||
|
||||
```rust
|
||||
#[derive(Copy)]
|
||||
struct A {
|
||||
a: u32,
|
||||
@@ -36,7 +37,7 @@ struct A {
|
||||
```TOML
|
||||
[workspace]
|
||||
members = [
|
||||
"./main",
|
||||
"./main",
|
||||
"./impl-derive",
|
||||
"./my-trait",
|
||||
]
|
||||
@@ -45,7 +46,8 @@ members = [
|
||||
### (1)实现my-trait
|
||||
- 运行cargo new my-trait --lib创建;
|
||||
- 编写src/lib.rs代码如下:
|
||||
```Rust
|
||||
|
||||
```rust
|
||||
// my-trait/src/lib.rs
|
||||
pub trait MyTrait{ // 定义MyTrait
|
||||
fn do_something();
|
||||
@@ -68,7 +70,7 @@ edition = "2021"
|
||||
# 下面的几行为添加的内容
|
||||
#######################
|
||||
[lib]
|
||||
proc-macro = true
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
syn = "2.0.15"
|
||||
@@ -76,7 +78,7 @@ quote = "1.0.26"
|
||||
```
|
||||
|
||||
- 编写src/lib.rs如下:
|
||||
```Rust
|
||||
```rust
|
||||
// impl-derive/src/lib.rs
|
||||
extern crate proc_macro;
|
||||
use crate::proc_macro::TokenStream;
|
||||
@@ -122,7 +124,7 @@ impl-derive = {path = "../impl-derive"} # 添加这行
|
||||
```
|
||||
|
||||
- 编写src/lib.rs:
|
||||
```Rust
|
||||
```rust
|
||||
// main/src/lib.rs
|
||||
use my_trait::MyTrait;
|
||||
use impl_derive::MyDeriveMacro;
|
||||
@@ -162,7 +164,7 @@ proc-macro = true # 添加这行
|
||||
```
|
||||
|
||||
- 编写impl-fn-macro/src/lib.rs如下:
|
||||
```Rust
|
||||
```rust
|
||||
// impl-fn-macro/src/lib.rs
|
||||
use proc_macro::TokenStream;
|
||||
|
||||
@@ -182,7 +184,7 @@ impl-fn-macro = {path = "../impl-fn-macro"} # 添加这行
|
||||
```
|
||||
|
||||
- 编写main/src/main.rs代码如下:
|
||||
```Rust
|
||||
```rust
|
||||
// main/src/main.rs
|
||||
use impl_fn_macro::make_answer;
|
||||
make_answer!(); // 调用函数宏生成answer函数
|
||||
@@ -216,7 +218,7 @@ impl-attr-macro中实现了类属性宏func_info。
|
||||
...
|
||||
|
||||
## 添加下面2行
|
||||
[lib]
|
||||
[lib]
|
||||
proc_macro = true
|
||||
|
||||
[dependencies]
|
||||
@@ -227,7 +229,7 @@ proc-macro2 = "1.0.56"
|
||||
```
|
||||
|
||||
- 编辑impl-attr-macro/src/lib.rs文件如下:
|
||||
```Rust
|
||||
```rust
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{parse_macro_input, ItemFn};
|
||||
@@ -261,7 +263,7 @@ impl-attr-macro = {path = "../impl-attr-macro"} # 添加这行
|
||||
```
|
||||
|
||||
- 编写main/src/main.rs代码如下:
|
||||
```Rust
|
||||
```rust
|
||||
// main/src/main.rs
|
||||
use impl_attr_macro::func_info;
|
||||
|
||||
@@ -272,6 +274,5 @@ fn foo() {
|
||||
|
||||
fn main() {
|
||||
foo(); // 会自动加上属性宏中的内容
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
fn关键字、函数名、函数参数名及其类型(如果有的话)、返回值类型(如果有的话)组成函数签名, 加上由一对花括号包含的函数体组成函数。例子如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
// 一个没有参数,也没有返回值的函数
|
||||
fn print_line() {
|
||||
println!("++++++++++++++++");
|
||||
@@ -32,7 +32,7 @@ fn main() {
|
||||
|
||||
Rust中,函数也可以定义在函数内部,如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn calculate(a: u32, b: u32) {
|
||||
println!("a is {:?}", a);
|
||||
println!("b is {:?}", b);
|
||||
@@ -57,7 +57,7 @@ fn main() {
|
||||
|
||||
Rust中,语句是执行一个写操作但不返回值的指令,表达式则计算并产生一个值。
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn main() {
|
||||
let a = 1u32; // "1u32"就是一个表达式, “let a = 1u32;”则是一个语句
|
||||
let b = a + 1;// “a + 1”就是一个表达式,“let b = a + 1;”则是一个语句
|
||||
@@ -74,7 +74,7 @@ fn main() {
|
||||
|
||||
- 使用return指定返回值,如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn sum(a: u32, b: u32) -> u32 {
|
||||
let r = a + b;
|
||||
return r //可以加分号,也可以不加分号, 所以这行等价于“return r;”
|
||||
@@ -89,7 +89,7 @@ fn main() {
|
||||
```
|
||||
|
||||
特别的return关键字不指定值时,表示返回的是(),如下:
|
||||
```Rust
|
||||
```rust
|
||||
fn my_function() -> () {
|
||||
println!("some thing");
|
||||
return; //等价于 “return ()”
|
||||
@@ -98,7 +98,7 @@ fn main() {
|
||||
|
||||
- 不使用return关键字,将返回最后一条执行的表达式的计算结果,如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn sum(a: u32, b: u32) -> u32 {
|
||||
println!("a is {:?}", a);
|
||||
println!("b is {:?}", b);
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
示例如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
/*
|
||||
* 块注释:
|
||||
* 函数名:sum
|
||||
@@ -48,7 +48,7 @@ Rust提供了cargo doc命令可以把文档注释转换成html网页,最终展
|
||||
|
||||
示例如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
// 下面是文档行注释
|
||||
|
||||
/// `add_one` 将指定值加1
|
||||
|
||||
@@ -11,7 +11,7 @@ Rust中的控制流结构主要包括:
|
||||
|
||||
- `if`执行条件判断,示例如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn main() {
|
||||
let a = 2u32;
|
||||
|
||||
@@ -25,7 +25,7 @@ Rust中的控制流结构主要包括:
|
||||
|
||||
- `if - else if`处理多重条件,示例如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn main() {
|
||||
let a = 3u32;
|
||||
|
||||
@@ -49,7 +49,7 @@ Rust中的控制流结构主要包括:
|
||||
|
||||
`if`是一个表达式,所以可以在`let`右侧使用,如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn main() {
|
||||
let a = 3u32;
|
||||
|
||||
@@ -71,7 +71,7 @@ Rust中的控制流结构主要包括:
|
||||
|
||||
- loop重复执行代码
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn main() {
|
||||
// 一直循环打印 again
|
||||
loop {
|
||||
@@ -82,7 +82,7 @@ Rust中的控制流结构主要包括:
|
||||
|
||||
- 使用`break`终止循环
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn main() {
|
||||
let mut counter = 0;
|
||||
|
||||
@@ -97,7 +97,7 @@ Rust中的控制流结构主要包括:
|
||||
}
|
||||
```
|
||||
上面的代码将打印10次,遇到`break`后终止循环。另外,`break`也可以返回值,如下:
|
||||
```Rust
|
||||
```rust
|
||||
fn main() {
|
||||
let mut counter = 0;
|
||||
|
||||
@@ -115,7 +115,7 @@ Rust中的控制流结构主要包括:
|
||||
|
||||
- 使用`continue`可以直接跳到下一轮循环
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn main() {
|
||||
let mut x = 0;
|
||||
// 此循环将只打印10以内的奇数
|
||||
@@ -136,7 +136,7 @@ Rust中的控制流结构主要包括:
|
||||
|
||||
- `while`条件循环执行代码,当条件不满足后结束循环,如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn main() {
|
||||
let mut cnt = 0u32;
|
||||
while cnt < 10 {
|
||||
@@ -148,7 +148,7 @@ Rust中的控制流结构主要包括:
|
||||
|
||||
- 在`while`循环中也可以使用`break`和`continue`,如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn main() {
|
||||
let mut x = 0;
|
||||
|
||||
@@ -169,7 +169,7 @@ Rust中的控制流结构主要包括:
|
||||
|
||||
`for`循环用来对一个集合的每个元素执行一些代码,使用方式如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn main() {
|
||||
let a = [10, 20, 30, 40, 50];
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ Rust程序使用的内存空间分为如下:
|
||||
“栈和栈帧属于操作系统的概念,由操作系统进行管理,栈空间以后进先出的顺序存储数据。将数据放到栈上就做入栈,将数据移出栈就做出栈。
|
||||
每次调用函数时,操作系统会在栈顶创建一个栈帧来保存函数的上下文数据(主要是函数内部声明的局部变量),函数返回时返回值也会存储在该栈帧中。当函数调用者取得该函数返回值后,栈帧会被释放。”引用自《Rust入门秘籍》。
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn f1(a: i32, b: i32) -> i32 {
|
||||
let c: i32 = 1;
|
||||
let r = a + b + c;
|
||||
|
||||
@@ -10,7 +10,7 @@ Rust所有权的规则如下:
|
||||
- 值在任何时刻有且仅有一个所有者;
|
||||
- 当所有者离开作用域后,这个值将丢弃。
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn main() {
|
||||
let a: u32 = 8;
|
||||
let b: String = String::from("hello");
|
||||
@@ -28,7 +28,7 @@ fn main() {
|
||||
|
||||
示例1:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn f() {
|
||||
let b = 1u32; // --------------------------------|
|
||||
let c = 2u32; //-----------| |
|
||||
@@ -50,7 +50,7 @@ fn main() {
|
||||
|
||||
示例2:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn main() {
|
||||
let a = 8u32; // --------------------------|
|
||||
{ // |
|
||||
@@ -72,7 +72,7 @@ fn main() {
|
||||
- `to_string`
|
||||
- `String::new`
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn main() {
|
||||
let s1 = String::from("Hello"); // 方法一
|
||||
let s2 = "Hello".to_string(); // 方法二
|
||||
@@ -95,7 +95,7 @@ fn main() {
|
||||
|
||||
Rust标准库中,String类型的定义如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
pub struct String {
|
||||
vec: Vec<u8>,
|
||||
}
|
||||
@@ -146,7 +146,7 @@ struct String {
|
||||
|
||||
在Rust中,编译时大小确定的数据放在栈上,编译时大小不能确定的数据放在堆上。考虑如下代码:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn main() {
|
||||
let mut s = String::new();
|
||||
s.push('A');
|
||||
@@ -169,7 +169,8 @@ Rust所有权规则第二条,在任意时刻,值有且仅有一个所有者
|
||||
### 4.1 完全存储在栈上的类型
|
||||
|
||||
考虑如下代码:
|
||||
```Rust
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let x = 5u32;
|
||||
let y = x;
|
||||
@@ -183,7 +184,7 @@ x和y都是u32类型,在编译时知道大小,都存储在栈上。代码第
|
||||
|
||||
再考虑如下代码:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn main() {
|
||||
let s = "Hello world!".to_string();
|
||||
let s1 = s;
|
||||
@@ -222,7 +223,8 @@ s是`String`类型,字符串`"Hello world"`是存储在堆内存上的,其
|
||||
## 6. Clone
|
||||
|
||||
当需要拷贝堆上的数据时,可以使用`clone`方法,完成深拷贝的操作,如下:
|
||||
```Rust
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let s = "Hello world!".to_string();
|
||||
let s1 = s.clone(); // 这将发生深拷贝
|
||||
@@ -250,7 +252,7 @@ Rust中,默认实现了`Copy trait`的类型有:
|
||||
### 8.1 将值传给函数
|
||||
|
||||
在将值传递给函数时,和变量赋值一样会发生值的移动(或复制),如下:
|
||||
```Rust
|
||||
```rust
|
||||
fn main() {
|
||||
let s = String::from("hello");
|
||||
takes_ownership(s);
|
||||
@@ -274,7 +276,7 @@ fn makes_copy(some_integer: i32) {
|
||||
|
||||
函数的返回值也可以转移所有权,如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn main() {
|
||||
let s1 = gives_ownership(); // gives_ownership 将返回值转移给 s1
|
||||
let s2 = String::from("hello"); // s2 进入作用域
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
考虑如下代码:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn main() {
|
||||
let s2 = String::from("hello");
|
||||
print(s2);
|
||||
@@ -19,7 +19,7 @@ fn print(s: String) {
|
||||
|
||||
如果要在调用`print`函数后仍然能使用s2,根据本书目前学过的Rust知识,则需要将所有权再从函数转移到变量,然后使用,代码如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn main() {
|
||||
let s2 = String::from("hello");
|
||||
let s3 = print(s2);
|
||||
@@ -38,7 +38,7 @@ fn print(s: String) -> String {
|
||||
|
||||
引用本质上是一个指针,它存储一个地址,通过它可以访问存储在该地址上属于其它变量的数据。与指针不同的是,引用确保指向某个特性类型的有效值。对于一个变量的引用就是在此变量前面加上&符合。
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
let a = 5u32;
|
||||
let b = &a; // b是对a的引用
|
||||
|
||||
@@ -51,7 +51,7 @@ fn print(s: String) -> String {
|
||||

|
||||
|
||||
**获取变量的引用,称之为借用** 。通过借用,允许使用被引用变量绑定的值,同时又没有移动该变量的所有权。前面的示例代码可以变成如下:
|
||||
```Rust
|
||||
```rust
|
||||
fn main() {
|
||||
let s2 = String::from("hello");
|
||||
let s3 = &s2; //s3是对s2的借用,s3并不拥有String::from("hello")的所有权,s2的所有权没有改变
|
||||
@@ -66,7 +66,7 @@ fn print(s: &String) {
|
||||
|
||||
在一个范围对变量进行多个引用是可以的,如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn main() {
|
||||
let s2 = String::from("hello");
|
||||
let s3 = &s2; //s3是对s2的借用,s3并不拥有String::from("hello")的所有权,s2的所有权没有改变
|
||||
@@ -85,7 +85,7 @@ fn print(s: &String) {
|
||||
|
||||
引用只能使用变量,并不允许改变变量的值,如果需要改变变量,需要使用可变引用(下节内容),下面的代码会报错:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn main() {
|
||||
let s = String::from("hello");
|
||||
change(&s);
|
||||
@@ -103,7 +103,7 @@ fn change(some_string: &String) {
|
||||
### 2.1 使用可变引用
|
||||
|
||||
可以通过可变引用改变变量的值,对一个变量加上`&mut`就是对其的可变引用,示例如下:
|
||||
```Rust
|
||||
```rust
|
||||
fn main() {
|
||||
let mut s = String::from("hello");
|
||||
change(&mut s);
|
||||
@@ -153,7 +153,7 @@ fn change(some_string: &mut String) {
|
||||
|
||||
(1)限制一:**同一作用域,特定数据只能有一个可变引用**。如下代码会报错:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn main() {
|
||||
let mut s1 = String::from("hello");
|
||||
let r1 = &mut s1; // 可变引用
|
||||
@@ -164,7 +164,7 @@ fn main() {
|
||||
|
||||
但是下面的代码可以的(新老编译器都可以):
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn main() {
|
||||
let mut s = String::from("hello");
|
||||
|
||||
@@ -182,7 +182,7 @@ fn main() {
|
||||
|
||||
下面的代码在新编译器中也是可以的:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn main() {
|
||||
let mut s = String::from("hello");
|
||||
|
||||
@@ -198,7 +198,7 @@ fn main() {
|
||||
|
||||
(2)限制二:**同一作用域,可变引用和不可变引用不能同时存在**。如下代码编译错误:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn main() {
|
||||
let mut s = String::from("hello");
|
||||
let r1 = &s; // 没问题
|
||||
@@ -211,7 +211,7 @@ fn main() {
|
||||
|
||||
下面的代码在新编译器中是可以的:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn main() {
|
||||
let mut s = String::from("hello");
|
||||
|
||||
@@ -277,7 +277,7 @@ int main()
|
||||
|
||||
如下代码因为会产生悬垂引用,编译将不会通过:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn main() {
|
||||
let reference_to_nothing = dangle();
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ Slice(切片)类型,表示引用集合中一段连续的元素序列。Sli
|
||||
|
||||
Rust中几乎总是使用切片数据的引用。切片数据的引用对应的数据类型描述为`&[T]`或`&mut [T]`,前者不可通过Slice引用来修改源数据,后者可修改源数据。示例如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn main(){
|
||||
let mut arr = [11,22,33,44];
|
||||
|
||||
@@ -36,7 +36,7 @@ Slice类型是一个胖指针,包含两个字段:
|
||||
|
||||
`String`的切片类型为`&str`而不是`&String`,其使用方式如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn main() {
|
||||
let s = String::from("hello world!");
|
||||
let s1 = &s[6..]; // 切片类型&str
|
||||
@@ -54,7 +54,7 @@ fn main() {
|
||||
|
||||
数组的Slice,如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn main() {
|
||||
let a: [u32; 5] = [1, 2, 3, 4, 5];
|
||||
let b = &a[1..3];
|
||||
@@ -64,7 +64,7 @@ fn main() {
|
||||
|
||||
Vec的Slice,如下:
|
||||
|
||||
```Rust
|
||||
```rust
|
||||
fn main() {
|
||||
let v: Vec<u32> = vec![1, 2, 3, 4, 5];
|
||||
let b = &v[1..3];
|
||||
|
||||
Reference in New Issue
Block a user