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

|

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

|

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

|

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

|

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

|

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

|

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