fix Rust rust code

This commit is contained in:
Davirain
2023-05-29 10:04:53 +08:00
parent 9a7df7b5b1
commit 77ceb0039c
33 changed files with 290 additions and 200 deletions

View File

@@ -165,7 +165,7 @@ cargo new hello_world
默认情况下main.rs文件中已经包含了一个简单的`Hello, World`程序,代码如下:
```Rust
```rust
fn main() {
println!("Hello, world!");
}

View File

@@ -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绑定到变量aa为不可变变量
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

View File

@@ -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"),

View File

@@ -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>>,
}

View File

@@ -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

View File

@@ -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;

View 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;
```

View File

@@ -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);

View File

@@ -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() {
![注释](.././assets/22.png)
此时就需要使用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);
}
```

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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() {
![注释](.././assets/27.png)
前面共享列表的需求则可以使用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,

View File

@@ -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是可变引用

View File

@@ -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;

View File

@@ -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 modulemodule名字和文件名字同名

View File

@@ -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);
}

View File

@@ -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() {

View File

@@ -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]

View File

@@ -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
![注释](.././assets/60.png)
## 5. 忽略某个测试
有时候运行cargo test时想忽略其中的某个测试此时可以通过使用ignore属性来标记该测试来排除它。例如有如下测试代码
```Rust
```rust
pub fn add_two(a: i32) -> i32 {
a + 2
}

View File

@@ -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;
![注释](.././assets/41.png)
而下面的示例因为没有将所有的包注释放在文件最上面,所以会报错:
```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;
```

View File

@@ -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(){
4Rust使用`0b`表示二进制、`0o`表示八进制、`0x`表示十六进制,如下:
```Rust
```rust
fn main(){
let a: u32 = 0b101; // 二进制整数
let b: i32 = 0o17; // 八进制整数
@@ -68,7 +68,7 @@ fn main(){
5Rust中所有的数值类型都支持基本数学运算加、减、乘、除、取余如下
```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);

View File

@@ -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;

View File

@@ -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相关的功能
![注释](.././assets/52.png)
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对这个工程编译运行。

View File

@@ -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提供的函数

View File

@@ -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 ),* ) => {

View File

@@ -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(); // 会自动加上属性宏中的内容
}
}
```

View File

@@ -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);

View File

@@ -18,7 +18,7 @@
示例如下:
```Rust
```rust
/*
* 块注释:
* 函数名sum
@@ -48,7 +48,7 @@ Rust提供了cargo doc命令可以把文档注释转换成html网页最终展
示例如下:
```Rust
```rust
// 下面是文档行注释
/// `add_one` 将指定值加1

View File

@@ -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];

View File

@@ -20,7 +20,7 @@ Rust程序使用的内存空间分为如下
“栈和栈帧属于操作系统的概念,由操作系统进行管理,栈空间以后进先出的顺序存储数据。将数据放到栈上就做入栈,将数据移出栈就做出栈。
每次调用函数时操作系统会在栈顶创建一个栈帧来保存函数的上下文数据主要是函数内部声明的局部变量函数返回时返回值也会存储在该栈帧中。当函数调用者取得该函数返回值后栈帧会被释放。”引用自《Rust入门秘籍》。
```Rust
```rust
fn f1(a: i32, b: i32) -> i32 {
let c: i32 = 1;
let r = a + b + c;

View File

@@ -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 进入作用域

View File

@@ -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 {
![注释](.././assets/10.png)
**获取变量的引用,称之为借用** 。通过借用,允许使用被引用变量绑定的值,同时又没有移动该变量的所有权。前面的示例代码可以变成如下:
```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();
}

View File

@@ -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];