Rust note

Rust

《Rust 权威指南》读书笔记,本文将持续跟进

基础篇

Hello, world!

fn main() {
    println!("Hello, world!");
}

println!是一个宏,负责打印。

变量声明

使用let 关键字进行声明:
let 变量名: 类型;
也可以同时赋初始值:
let 变量名: 类型 = 值;
编译器可对部分值的类型进行推导:
let 变量名 = 值;

变量类型——标量类型

1.整型

i8、i16、i32,i64、i128、isize
u8、u16、u32、u64、u128、usize

isize、usize为平台相关类型

let num: i32 = 100;

debug 模式下变量溢出会panic
release 模式下变量溢出会形成环绕,即最大数+1会变成该类型最小的数。若想显示环绕则使用标准库Wrapping类型。

整数字面量示例
Decimal999_999
Hex0xff
Octal0o77
Binary0b1010_0101
Byte (u8 only)b’Z’

默认推导类型:i32

2.浮点型

f32、f64

let num: f32 = 100.00;

浮点数遵循IEEE-754浮点数标准

默认推导类型:f64

3.布尔型

bool
let is_false: bool = false;

4.字符类型

char
let ch: char = 'a';

变量类型——复合类型

两种内置符合类型

1.元组类型

定义:
let tup: (i32, f32, bool) = (1, 2.0, true);
访问:
通过元组名.索引方式访问
tup.0、tup.1、tup.2
解构:
将元组拆分为多个变量
let (a, b, c) = tup;

2.数组类型

定义:
let a = [1, 2, 3, 4, 5, 6];
let a: [i32;6] = [1, 2, 3, 4, 5, 6];
let a: [3;6];等价于let a = [3, 3, 3, 3, 3, 3];
访问:
通过索引访问
a[0]、a[1]

变量的可变性与不可变性

使用以上方式声明的变量均不可修改,对其赋值会提示编译错误,其体现了变量的不可变性。

error[E0384]: cannot assign twice to immutable variable `a`
  --> src\main.rs:31:5
   |
30 |     let (a,b,c) = tup;
   |          -
   |          |
   |          first assignment to `a`
   |          help: consider making this binding mutable: `mut a`
31 |     a = 100;
   |     ^^^^^^^ cannot assign twice to immutable variable

若想声明可变的变量则在let 后加 mut 关键字(mut: mutable)。
如:let mut a: i32 = 100;此时对a赋值a = 200;是允许的。

变量的隐藏性

同一作用域内可以声明同名变量,下边的变量会隐藏上边的变量。即使类型不同也是可以的。
let a: i32 = 100;
let a: f32 = 100.0;

常量

定义:
const MAX_POINTS: u32 = 100_000;
访问:
可访问,不可修改。

函数

Rust 使用函数使用“蛇形命名”,即小写字母和下划线的组合方式。
无参无返回值形式

fn hello_rust() {
   println!("hello, rust!")
}

有参有返回值形式

fn add_one(num :i32) -> i32 {
   return num + 1;
}

函数内部也可以定义函数

fn main() {
    fn add_one(num :i32) -> i32 {
        return num + 1;
    }
    println!("{}",add_one(1));
}

使用表达式作为函数返回值,不需写return和结尾分号

fn main() {
    println!("{}",add_one(1)); // 2
}

fn add_one(num :i32) -> i32 {
    num + 1
}

处理多返回值可使用元组

fn f() -> (i32, i64, f32 ) {
   (1, 2, 3.0)
}
语句

语句以英文半角下的“;”结尾。

表达式
fn main() {
    let x = 15;
    let y = {
        let x = 20 + 1;
        x + 5
    };
    println!("{}",y);
}

x + 6 称之为表达式,x = 后边的20 + 1也是表达式(有点像数学公式) ,表达式一般用一对{}(表示代码块或作用域)括起来,注意表达式一般写在代码块的最后,其后不写分号,写分号就变成了语句。

注释

// 单行注释,可放在语句末尾,分号后

/* 多行注释放在单行使用 */

/*
 * 多行注释
 * 多行注释
 * 多行注释
 */

/// #这是文档注释
/// ## 可以写一些特殊的文本格式,如markdown
/// ```
///  fn f() -> (i32, i64, f32 ) //返回三个值
/// ```
fn f() -> (i32, i64, f32 ) {
    (1, 2, 3.0)
}

控制流

if else

if

    let a = 3;
    if a > 2 {
        println!("a > 2");
    }

if else if

    let a = 3;
    if a > 4 {
        println!("a > 4");
    }else if a > 3{
        println!("a > 3");
    }else {
        println!("what ?");
    }

作为表达式使用,表达式中的值必须类型统一。如100、200。不可以为100,“hello”。

    let a = 3;
    let result = if a > 3 { 
        100 
    }else { 
        200
    };
循环

rust 支持3种循环loop,for,while
break 用于打破循环,即跳出当前循环
continue 结束当前层循环

loop

    let mut counter = 0;
    loop {
        counter += 1;
        if counter == 100 {
            println!("循环了100次");
            break;
        }
    }

作为表达式使用

    let mut counter = 0;
    let result = loop {
        counter += 1;
        if counter == 10 {
            break counter * 2; // 表达式的值
        }
    };
    println!("The result is {}", result);

while

    let mut number = 3;
    while number != 0 {
        println!("{}", number);
        number = number - 1;
    }

结束循环,跳转到tag处,常用于打破多重循环

    let mut number = 3;
    'tag: while number != 0 {
        if number == 1 {
            break 'tag;
        }
        println!("{}", number);
        number = number - 1;
    }

for

迭代数组
iter() 为迭代器

    let a = [10, 20, 30, 40, 50];
    for element in a.iter() {
        println!("the value is: {}", element);
    }

正向遍历与反向遍历
1…4 表示范围[1,4),左闭右开,步长为1的从左往右遍历方式
(1…4).rev():反向输出[1,4)的值

    for number in 1..4 {
        println!("{}", number);
    }

    for number in (1..4).rev() {
        println!("{}", number);
    }

所有权

Rust使用包含特定规则的所有权系统来管理内存,这套规则允许编译器在编译过程中执行检查工作,而不会产生任何的运行时开销。

所有权规则

Rust中的每一个值都有一个对应的变量作为它的所有者 。
在同一时间内,值有且仅有一个所有者。
当所有者离开自己的作用域时,它持有的值就会被释放掉。

变量作用域

Rust 用“{}”代表作用域。
变量在进入作用域后变得有效。它会保持自己的有效性直到自己离开作用域为止。

{  // 未定义变量s
   let s = String::from("hello!"); // 变量s生效
} // 变量s离开作用域,Rust自动调用drop函数,销毁变量,归还内存给操作系统
变量和数据交互的方式——移动(所有权转移)

标量类型,因编译时期就可以明确的知道其占用的内存大小,不会涉及堆内存分配,将其完整的放在栈空间即可。所以b = a;复制了a 底层的数据3,两个3都在栈空间,相互独立。

    let mut a = 3;
    let mut b = a;

    a += 100;
    println!("{}",a); // 103
    println!("{}",b); // 3

变量的移动(所有权转移)
以下代码设计到String的底层结构,String是一个既有栈上内存占用,又有堆上内存占用的数据结构,其发生赋值操作s2 = s1时,会将s1的栈上内容复制一份,给s2,同时将堆上内存的指针转移给s2。s2的出现代替了s1的存在,s1在发生赋值后,便不再可用。这样设计巧妙的避开了同一个内存区域被两次drop。
这其实就是一种浅拷贝操作,但会弃用被拷贝的变量。Rust称上述动作为移动(move)。Rust永远不会自动地创建数据的深度拷贝。因此在Rust中,任何自动的赋值操作都可以被视为高效的。

    let s1 = String::from("hello");
    let s2 = s1;

    println!("{}",s1);
    println!("{}",s2);
error[E0382]: borrow of moved value: `s1`
  --> src\main.rs:31:19
   |
28 |     let s1 = String::from("hello");
   |         -- move occurs because `s1` has type `String`, which does not implement the `Copy` trait
29 |     let s2 = s1;
   |              -- value moved here
30 |
31 |     println!("{}",s1);
   |                   ^^ value borrowed here after move
   |
   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
变量和数据交互的方式——克隆

深度拷贝,调用其clone()方法

   let s1 = String::from("hello");
   let s2 = s1.clone();
   println!("s1 = {}, s2 = {}", s1, s2);
栈上数据赋值——实现Copy trait

trait 类似于其他语言的接口,描述了一系列行为。
Copy trait 负责栈上数据的拷贝工作,实现该trait即可将值复制给其他变量,原变量仍可用。
Drop trait 负责堆内存的释放工作。
同一种类型对于 Copy 和 Drop 只能实现其一。
内置的标量类型整型、浮点型、布尔型、字符型数据都实现了Copy trait。若元组中的所有数据类型都实现了Copy trait,那么该元组也具有被Copy的能力。
完整的存储在栈上的类型都可以实现Copy,只要有堆内存和引用其他资源的就不可以实现Copy,否则编译时报错。
用Copy 或 clone的方式对只保留在栈上的类型的数据进行复制都是可以的。

所有权与函数,参数、返回值、作用域

当函数的参数类型实现了Copy,那么传给函数参数的变量,在调用函数后,仍可使用。反之,则发生了所有权转移,参数的作用域即函数的作用域。在函数执行结束后,会自动调用drop,实现回收。所以,在函数调用处之后,是不可使用因调用函数而发生了所有权转移的变量的。若想继续使用该变量,可将其作为函数的返回值,返回后重新使用。引用可以解决这个壁垒。

引用

Rust 使用 & 代表引用语义,使用 * 代表解引用语义。
引用不持有所有权。

不可变引用——借用
    let s1 = String::from("hello");
    let s2 = &s1;

    println!("{}",s1);
    println!("{}",s2);

上述代码s2持有了s1的引用。
引用类型作为函数的参数

fn length(text: &String) -> usize {
    text.len()
}

调用上述length函数,不会发生所有权转移,在函数退出时,不销毁传入的参数text。
不可变的引用也称之为借用(borrow)。对length函数中的text参数进行修改是不被允许的。

可变引用

作为函数参数,表示为s: &mut String

fn change_string(s: &mut String) {
    s.push_str("_修改后的.");
}
    let mut s1 = String::from("hello");
    change_string(&mut s1);
    println!("{}",s1);

以下代码比较难以理解。在使用可变引用时比较迷惑人。
同一时刻,一个变量只能有一个可变引
如下操作不被允许

    let mut s1 = String::from("hello");
    let mut s2 = &mut s1; //------
    s1.push_str("_1"); //        | 可变引用s2,被操作时,不能交叉操作s1
    s2.push_str("_2"); //--------
error[E0499]: cannot borrow `s1` as mutable more than once at a time
  --> src\main.rs:38:5
   |
37 |     let mut s2 = &mut s1;
   |                  ------- first mutable borrow occurs here
38 |     s1.push_str("_1");
   |     ^^^^^^^^^^^^^^^^^ second mutable borrow occurs here
39 |     s2.push_str("_2");
   |     ----------------- first borrow later used here

如下操作同样不被允许

	let mut s1 = String::from("hello");
    let mut s2 = &mut s1;
    let mut s3 = &mut s1;
    s2.push_str("hh");
error[E0499]: cannot borrow `s1` as mutable more than once at a time
  --> src\main.rs:38:18
   |
37 |     let mut s2 = &mut s1;
   |                  ------- first mutable borrow occurs here
38 |     let mut s3 = &mut s1;
   |                  ^^^^^^^ second mutable borrow occurs here
39 |     s2.push_str("hh");
   |     ----------------- first borrow later used here

如下操作被允许

    let mut s1 = String::from("hello");
    let mut s2 = &mut s1;
    s2.push_str("_2");
    s1.push_str("_1");

使用作用域隔离可变引用

    let mut s1 = String::from("hello");
    {
        let mut s2 = &mut s1;
        s2.push_str("hh");
    }
    let mut s3 = &mut s1;
    println!("{}",s3);
    println!("{}",s1);

发生数据竞争的条件:
1.两个或两个以上的指针同时访问同一空间。
2.其中至少有一个指针会向空间中写入数据。
3.没有同步数据访问的机制。

操作可变引用后,不可以操作原来的不可变引用
允许的

    let mut s1 = String::from("hello");
    let r1 = &s1;
    let r2 = &s1;
    println!("{}-{}",r1,r2);
    let r3 = &mut s1;
    println!("{}",r3);

不允许的

    let mut s1 = String::from("hello");
    let r1 = &s1;
    let r2 = &s1;
    println!("{}-{}",r1,r2);
    let r3 = &mut s1;
    println!("{}",r3);
    println!("{}",r1);
error[E0502]: cannot borrow `s1` as mutable because it is also borrowed as immutable
  --> src\main.rs:40:14
   |
37 |     let r1 = &s1;
   |              --- immutable borrow occurs here
...
40 |     let r3 = &mut s1;
   |              ^^^^^^^ mutable borrow occurs here
41 |     println!("{}",r3);
42 |     println!("{}",r1);
   |                   -- immutable borrow later used here
悬垂引用

未引入生命周期,悬垂引用不被允许

fn main() {
    let reference_to_nothing = dangle();
}

fn dangle() -> &String {
    let s = String::from("hello");
    &s
}
error[E0106]: missing lifetime specifier
  --> src\main.rs:39:16
   |
39 | fn dangle() -> &String {
   |                ^ expected named lifetime parameter
   |
   = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
help: consider using the `'static` lifetime
   |
39 | fn dangle() -> &'static String {
   |                 +++++++
引用规则

在任何一段给定的时间里,要么只能拥有一个可变引用,要么只能拥有任意数量的不可变引用,引用总是有效的。

切片

切片是一种特殊的引用形式。其不持有所有权。
切片允许我们引用集合中某一段连续的元素序列。
将整个集合转换成切片
let slice = 集合[..];
将集合一部分转换成切片
let slice = 集合[1..];// 从索引1到集合最后
let slice = 集合[1..2];// 从索引1到索引2
let slice = 集合[..2];// 从索引0到索引2
let slice = 集合[0..3];// 从索引0到索引3
切片中[start…end],start与end构成的是一个左闭右开的区间[start,end)

字符串切片
    let s = String::from("hello world");
    let hello = &s[0..5];
    let world = &s[6..11];
    println!("{}",hello);
    println!("{}",world);

对字符串切片时需考虑字符边界问题,UTF-8编码使用1-4字节表示一个字符。

字符串字面量就是切片。

   let s = "hello, world";
   let s :&str = "hello world";

str类型即字符串切片类型。

结构体

定义方式

struct 结构体名 {
    字段名0:类型,
    字段名1:类型,
}

访问方式

对象.字段名

定义User结构体

struct User {
    name: String,
    age: i32,
}

使用User结构体

    let user= User{
        name: "ophelia".to_string(),
        age:13,
    };
    println!("{:?}",user.name);
    println!("{:?}",user.age);

用函数方式创建User对象

fn new_user(name: String, age: i32) -> User {
     User{
         name:name,
         age: age,
    }
}

若函数形参名与结构体中字段重合,则不用写结构体的字段名,直接写入形参

fn new_user_two(name: String, age: i32) -> User {
    User{
        name,
        age,
    }
}
    let mut user = new_user(String::from("张三"),20);
    user.age = 22;
    println!("{}",user.age);

    let user = new_user_two(String::from("李四"),20);
    println!("{}",user.name);

使用已有的结构体中的字段
方式1

    let user = new_user_two(String::from("李四"),20);
    //使用其他user中的字段创建user
    let user1 = User{
        name:String::from("judy"),
        age:user.age,
    };

方式2 …运算符。语法糖。

    let user = new_user_two(String::from("李四"),20);
    //使用其他user中的字段创建user
    let user1 = User{
        name:String::from("julia"),
        ..user
    };

元组结构体

定义方式

struct 结构体名(类型1,类型2,类型3);

访问方式

对象.索引

定义元组结构体

    struct Color(i32, i32, i32);
    struct Point(i32, i32, i32);
    let black = Color(0, 0, 0);
    let origin = Point(0, 0, 0);
    println!("{}",black.0);
空结构体

定义方式

struct 结构体名 {}

空结构体一般用作实现trait。实现trait时,不需要使用结构体中的数据。
空结构体与空元组类似 let tup = ();

结构体数据的所有权

若结构体中所有字段都是非引用类型,那么只要结构体携带的数据时有效的,那么结构体就是有效的若结构体中有字段为引用类型,则需要引入生命周期的概念。

为结构体添加派生宏,Debug,实现打印功能

不在结构体上添加 #[derive(Debug)]是无法实现打印功能的。
还需要修改打印宏的占位符的参数 println!("{:?}",user); 加上:?

#[derive(Debug)]
struct User {
    name: String,
    age: i32,
}

let user= User{
    name: "ophelia".to_string(),
    age:13,
};

println!("{:?}",user); // User { name: "ophelia", age: 13 }

方法

方法与函数类似:都使用fn关键字及一个名称来进行声明;它们都可以拥有参数和返回值;另外,它们都包含了一段在调用时执行的代码。但是,方法与函数依然是两个不同的概念,因为方法总是被定义在某个结构体(或者枚举类型、trait对象)的上下文中,并且它们的第一个参数永远都是self,用于指代调用该方法的结构体实例。

定义方式

impl 结构体名 {
    fn 方法名0(self,参数1:类型) -> 返回值类型 {
        返回值
    }
    fn 方法名1(&self,参数1:类型,参数2:类型) -> 返回值类型 {
        返回值
    }
    fn 方法名2(&mut self,参数1:类型) -> 返回值类型 {
        返回值
    }
    
    fn 函数名(参数1:类型)-> 返回值类型 {
        返回值
    }
}

代码

#[derive(Debug)]
struct User {
    name: String,
    age: i32,
}

impl User {
    fn say(&self) {
        println!("say");
    }
    
    fn println(&mut self) {
        self.name = String::from("张三");
        println!("{:?}",self);
    }

    fn new_user(name:String, age:i32) ->User { // 关联函数,第一个参数非self,常用于结构体的构造器
        User{
            name,
            age,
        }
    }
}

fn main() {
    let mut user =  User{
        name:String::from("judy"),
        age: 16
    };
    let mut user = User::new_user(String::from("judy"),15);
    user.say();
    user.println();
}
关联函数

为结构体绑定函数,但是函数参数不传入self。则称之为关联函数,关联函数常用于创建对象。

访问方式一般为: let 变量名 = 结构体::关联函数名(参数);

impl User {
    fn say(&self) {
        println!("say");
    }

    fn println(&mut self) {
        self.name = String::from("张三");
        println!("{:?}",self);
    }

    fn new_user(name:String, age:i32) ->User { // 关联函数,第一个参数非self,常用于结构体的构造器
        User{
            name,
            age,
        }
    }
}
多impl块

Rust 支持将实现同一类型的方法,拆分到多个impl块中

impl User {
    fn get_name(self) ->String {
        self.name
    }
}
impl User {
    fn get_age(self) ->i32 {
        self.age
    }
}

枚举

定义
这里的变体就是枚举值

enum 枚举名 {
  变体1,
  变体2(类型),
}

访问
枚举名::变体名(参数)

变体名(参数)
代码
枚举中支持匿名结构体及空结构体struct Name;

#[derive(Debug)]
enum Paper {
    White(String),
    Black,
    Blue {x:i32,y:i32},
    Pink(i32,i32,i32)
}


fn main() {
    let p = Paper::Black;
    println!("{:?}",p);
    println!("{:?}",Paper::White(String::from("白色")));
}

可以将枚举类型加入结构体中。
也可为枚举类型绑定方法,与结构体绑定方法大同小异。

标准库中的枚举 Option

该枚举可以表示非空值与空值。他有Some(T),和None两个变体。Some表示值非空,None表示值为空

    let str1 = Some("hello");
    let str1 = None;
控制流运算符 match

match允许将一个值与一系列的模式相比较,并根据匹配的模式执行相应代码。模式可由字面量、变量名、通配符和许多其他东西组成。

    let op :Option<i32> = Some(128);

    match op {
        Some(op) => {
            println!("有值")
        },
        None => {
            println!("无值")
        }
    }

作为表达式使用
_ 代表通配符
_ => {} 为默认匹配模式,只有一条表达式时,{}可不写

    let value = 100;
    let result = match value  {
        10 => {
            10 + 2
        },
        90 => {
            90 + 3
        }
        _ => 0
    };

    println!("{}",result); // 0

包、单元包、模块

包与模块的划分是为了更好的管理、维护、复用代码。

包(package)

包:一个用于构建、测试并分享单元包的Cargo功能。
一个包(package)可以拥有多个二进制单元包及一个可选的库单元包。

单元包(crate)

单元包:一个用于生成库或可执行文件的树形模块结构。
可将部分代码拆分到独立的单元包(crate)中,并将它作为外部依赖进行
引用。

cargo new Rust创建项目Rust
项目结构

Rust
  |-------src
  |       |----------main.rs
  |-------target
  |       |----------debug
  |                    |------------一系列目录与文件,包括生成的可执行文件
  |--------Cargo.lock
  |--------Cargo.toml                         

Cargo会默认将src/main.rs 视作一个二进制单元包的根节点,这个二进制单元包与包拥有相同的名称。

模块(module) 及 use

它们被用于控制文件结构、作用域及路径的私有性。
通过定义模块来控制作用域及私有性:

mod 模块名 {
   // 代码...
}

pub mod 模块名 {
   // 代码...
}

模块中可以嵌套的定义模块。
通过使用模块,可以将相关的定义分到一组,并根据它们的关系指定有意义的名称。

路径(path)

一种用于命名条目的方法,这些条目包括结构体、函数和模块等。
用于在模块树中指明条目的路径:
路径有两种形式:
1.使用单元包名或字面量crate从根节点开始的绝对路径。
2.使用self、super或内部标识符从当前模块开始的相对路径。
绝对路径与相对路径都由至少一个标识符组成,标识符之间使用双冒号(::)分隔。
Rust中的所有条目(函数、方法、结构体、枚举、模块及常量)默认都是私有的。
使用pub关键字来暴露路径:

pub mod user_mod {

    pub mod action_mod {
        pub fn Do(){
            println!("do something!")
        }
    }

    #[derive(Debug)]
    pub struct User {
        name: String,
        age: i32,
    }

    impl User {
        fn say(&self) {
            println!("say");
        }

        pub fn println(self) {
            println!("{:?}",self);
        }

       pub fn new_user(name:String, age:i32) ->User { // 关联函数,第一个参数非self,常用于结构体的构造器
            User{
                name,
                age,
            }
        }

        fn get_name(self) ->String {
            self.name
        }

        fn get_age(self) ->i32 {
            self.age
        }
    }
}

使用super关键字开始构造相对路径

fn hello() {
    println!("hello!")
}
pub mod test {
    fn hello() {
        super::hello();
    }
}

将结构体或枚举声明为公共的

pub mod test_one {
    pub struct Person {
        pub name: String, // 需逐个公开
        age: i32,
    }
    
    pub enum Action { //枚举只要公开一个成员,即公开了所有成员
        Song,
        Jump,
        Rap,
    }
}

用use关键字将路径导入作用域
未使用use之前

fn main() {
    //绝对路径
    let user = crate::user_mod::User::new_user(String::from("张三"),12);
    user.println(); // User { name: "张三", age: 12 }

    //相对路径
    let user = user_mod::User::new_user(String::from("张三"),12);
    user.println(); // User { name: "张三", age: 12 }
}

使用use之后

use crate::user_mod::User;
fn main() {
    //绝对路径
    let user = User::new_user(String::from("张三"),12);
    user.println(); // User { name: "张三", age: 12 }

    //相对路径
    let user = User::new_user(String::from("张三"),12);
    user.println(); // User { name: "张三", age: 12 }
}

使用as关键字来提供新的名称

use crate::user_mod::User as U;
fn main() {
    //绝对路径
    let user = U::new_user(String::from("张三"),12);
    user.println(); // User { name: "张三", age: 12 }

    //相对路径
    let user = U::new_user(String::from("张三"),12);
    user.println(); // User { name: "张三", age: 12 }
}

使用pub use重导出名称
不仅将条目引入了作用域,而且使该条目可以被外部代码从新的作用域引入自己的作用域。

pub use crate::user_mod::User;

其他包在导入该包后,可访问该包因如的User。

使用外部包
更新Cargo.toml文件

Cargo.toml
[dependencies]
rand = "0.5.5"

在dependencies下指定包名,版本号,用use 导入即可使用。

使用嵌套的路径来清理众多use语句

use std::cmp::Ordering;
use std::io;

use std::{cmp::Ordering, io};

通配符*
use std::collections::*;

将模块拆分为不同的文件
原文件

// src/lib.rs
mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {}
    }
}

pub use crate::front_of_house::hosting;

pub fn eat_at_restaurant() {
    hosting::add_to_waitlist();
    hosting::add_to_waitlist();
    hosting::add_to_waitlist();
}

拆分后的文件

// src/lib.rs
mod front_of_house; // 声明front_of_house模块,让Rust前往与当前模块同名的文件中加载模块内容。
pub use crate::front_of_house::hosting;
pub fn eat_at_restaurant() {
    hosting::add_to_waitlist();
    hosting::add_to_waitlist();
    hosting::add_to_waitlist();
}
// src/front_of_house.rs
pub mod hosting;
// src/front_of_house/hosting.rs
pub fn add_to_waitlist() {}
工作空间(workspace)

该特性与Cargo相关,后续介绍

作用域(scope)

在编写代码的嵌套上下文中有一系列被定义在“作用域内”的名字。可以创建作用域并决定某个名字是否处于该作用域中,但是不能在同一作用域中使用相同的名字指向两个不同的条目;有一些工具可以被用来解决命名冲突。

通用集合类型

vector

动态数组(vector)可以让你连续地存储任意多个值。数据存储在堆上。

创建
let v: Vec<i32> = Vec::new();

使用宏创建并赋初值
let v = vec![1, 2, 3];

插入

 v.push(5);
v.push(6);
v.push(7);
v.push(8);

读取

let v = vec![1, 2, 3, 4, 5];
let third: &i32 = &v[2];
println!("The third element is {}", third);
match v.get(2) {
  Some(third) => println!("The third element is {}", third),
  None => println!("There is no third element."),
}

遍历

let v = vec![100, 32, 57];
for i in &v {
  println!("{}", i);
}
let mut v = vec![100, 32, 57];
for i in &mut v {
*i += 50;
}

使用枚举存储多个类型

enum SpreadsheetCell {
Int(i32),
Float(f64),
Text(String),
}
let row = vec![
SpreadsheetCell::Int(3),
SpreadsheetCell::Text(String::from("blue")),
SpreadsheetCell::Float(10.12),
];
String

字符串(string)是字符的集合。&str 是字符串切片。Rust的标准库中同时包含了其他一系列的字符串类型,比如OsString、OsStr、CString及CStr。
创建
let mut s = String::new();

创建字面量

let data = "initial contents";
let s = data.to_string();

从字面量生成
let s = String::from("initial contents");

更新 push_str 或 push

let mut s = String::from("foo");
s.push_str("bar");
s.push('a');

遍历

for c in "你好,world!".chars() {
   println!("{}", c);
}
for c in "你好,world!".bytes() {
   println!("{}", c);
}

索引
Rust 字符串使用UTF-8字符编码,字符长度不固定。不支持索引。

切片
做切片时应充分考虑边界问题。

HashMap

哈希映射(hash map)可以让你将值关联到一个特定的键上,
它是另外一种数据结构—映射 (map)的特殊实现。数据存储在堆上。

创建

use std::collections::HashMap;
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);

哈希映射与所有权
一旦键值对被插入,其所有权就会转移给哈希映射。假如我们只是将值的引用插入哈希映射,那么这些值是不会被移动到哈希映射中的。

访问哈希映射中的值

use std::collections::HashMap;
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);
let team_name = String::from("Blue");
let score = scores.get(&team_name);

更新哈希映射,旧值覆盖

use std::collections::HashMap;
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Blue"), 25);
println!("{:?}", scores);

只在键没有对应值时插入数据

use std::collections::HashMap;
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.entry(String::from("Yellow")).or_insert(50);
scores.entry(String::from("Blue")).or_insert(50);
println!("{:?}", scores);

基于旧值来更新值

use std::collections::HashMap;
let text = "hello world wonderful world";
let mut map = HashMap::new();
for word in text.split_whitespace() {
  let count = map.entry(word).or_insert(0);
  *count += 1;
}
println!("{:?}", map);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

metabit

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值