1.安装
官网 https://www.rust-lang.org/learn/get-started
2.猜数字游戏
use std::io;
use rand::{thread_rng, Rng};
use std::cmp::Ordering;
fn main() {
let secret_num = thread_rng().gen_range(1, 101);
loop {
println!("请猜一个数:");
let mut guess = String::new();
io::stdin().read_line(&mut guess).expect("无法读取");
println!("您猜的数字是:{}",guess);
let guess:u32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => continue
};
match guess.cmp(&secret_num){
Ordering::Less => println!("Too small"),
Ordering::Equal => {
println!("you win!");
break;
},
Ordering::Greater => println!("Too big"),
}
}
}
3.编程概念
3.1 变量可变性
fn main() {
let x = 5;
println!("The value of x is: {x}");
x = 6;//报错,变量如果要修改需要用mut修饰
println!("The value of x is: {x}");
}
//常量声明
const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;
//可以重复声明变量,后面的声明的变量覆盖前面的
let spaces = " ";
let spaces = spaces.len();
3.2数据类型
3.2.1 整数
Rust 有四种主要标量类型:整数、浮点数、布尔值和字符。
| 长度 | 签名 | 未签名 |
|---|---|---|
| 8位 | i8 |
u8 |
| 16位 | i16 |
u16 |
| 32位 | i32 |
u32 |
| 64位 | i64 |
u64 |
| 128位 | i128 |
u128 |
| 数字字面量 | 例子 |
|---|---|
| 十进制 | 98_222 |
| 十六进制 | 0xff |
| 八进制 | 0o77 |
| 二进制 | 0b1111_0000 |
| 字节 | b'A' |
3.2.2 浮点数
fn main() {
let x = 2.0; // f64
let y: f32 = 3.0; // f32
}
3.2.3 布尔类型
fn main() {
let t = true;
let f: bool = false; // with explicit type annotation
}
3.2.4 字符类型
fn main() {
let c = 'z';
let z: char = 'ℤ'; // with explicit type annotation
let heart_eyed_cat = '😻';
}
3.2.5 元组
fn main() {
let tup: (i32, f64, u8) = (500, 6.4, 1);
}
fn main() {
let tup = (500, 6.4, 1);
let (x, y, z) = tup;
println!("The value of y is: {y}");
}
fn main() {
let x: (i32, f64, u8) = (500, 6.4, 1);
//取值
let five_hundred = x.0;
let six_point_four = x.1;
let one = x.2;
}
3.2.6 数组
当知道元素数量不需要更改时,数组会更有用。例如,如果您在程序中使用月份名称,您可能会使用数组而不是向量,因为您知道它始终包含 12 个元素:
let months = ["January", "February", "March", "April", "May", "June", "July",
"August", "September", "October", "November", "December"];
可以使用方括号、每个元素的类型、分号以及数组中元素的数量来编写数组的类型,如下所示:
let a: [i32; 5] = [1, 2, 3, 4, 5];
这里,i32是每个元素的类型。分号后面的数字5 表示数组包含五个元素。
还可以通过指定初始值、后跟分号、然后在方括号中指定数组的长度来初始化数组,使其每个元素包含相同的值,如下所示:
let a = [3; 5];
指定的数组a将包含5所有元素,这些元素将全部设置为 3最初的值。这与写作相同let a = [3, 3, 3, 3, 3];,但方式更简洁。
fn main() {
let a = [1, 2, 3, 4, 5];
//访问数组中的值
let first = a[0];
let second = a[1];
}
3.3函数
3.3.1 函数的参数
fn main() {
another_function(5);
}
//必须声明参数的类型
fn another_function(x: i32) {
println!("The value of x is: {x}");
}
3.3.2 有返回值参数
fn main() {
let x = plus_one(5);
println!("The value of x is: {x}");
}
fn plus_one(x: i32) -> i32 {
//默认返回值不用写分号,如果要提前返回则用return 返回值;
x + 1
}
3.4流程控制
3.4.1 if和else
fn main() {
let number = 6;
if number % 4 == 0 {
println!("number is divisible by 4");
} else if number % 3 == 0 {
println!("number is divisible by 3");
} else if number % 2 == 0 {
println!("number is divisible by 2");
} else {
println!("number is not divisible by 4, 3, or 2");
}
}
fn main() {
let condition = true;
//条件赋值
let number = if condition { 5 } else { "six" };
println!("The value of number is: {number}");
}
3.4.2 loop循环
fn main() {
let mut counter = 0;
//带返回值
let result = loop {
counter += 1;
if counter == 10 {
break counter * 2;
}
};
println!("The result is {result}");
}
3.4.3 while循环
fn main() {
let mut number = 3;
while number != 0 {
println!("{number}!");
number -= 1;
}
println!("LIFTOFF!!!");
}
3.4.4 for循环
fn main() {
let a = [10, 20, 30, 40, 50];
for element in a {
println!("the value is: {element}");
}
}
fn main() {
for number in (1..4).rev() {
println!("{number}!");
}
println!("LIFTOFF!!!");
}
4.所有权
4.1 所有权规则
- Rust 中的每个值都有一个所有者。
- 一次只能有一位所有者。
- 当所有者超出范围时,该值将被删除。
fn main() {
let mut s = String::from("hello");
s.push_str(", world!"); // push_str() appends a literal to a String
//将s变量的值更换所有者为s1,更换后s将被销毁
let s1 = s;
println!("{s1}");
{
//将s1的值克隆一份给s2
let s2 = s1.clone();
println!("{s2}");
}
println!("{s1}");
}
4.2 变量不移动类型
let x = 5;
let y = x;
println!("x = {x}, y = {y}");
没有调用 clone,但x仍然有效并且没有移入y,该类型具有自动copy的特征
具有copy特征的类型
- 所有整数类型,例如
u32. - 布尔类型 ,
bool具有值true和false。 - 所有浮点类型,例如
f64. - 字符类型,
char. - 元组,如果它们仅包含也实现
Copy.例如,(i32, i32)实现Copy,但(i32, String)不实现。
4.3 引用
4.3.1 不可变引用
fn main() {
let s1 = String::from("hello");
let len = calculate_length(&s1);
println!("The length of '{s1}' is {len}.");
}
fn calculate_length(s: &String) -> usize {
s.len()
}
4.3.2 可变引用
fn main() {
let mut s = String::from("hello");
change(&mut s);
}
fn change(some_string: &mut String) {
some_string.push_str(", world");
}
同一域内不可出现多个可变引用,但可出现多个不可变引用
4.3.3 悬空引用
fn main() {
let reference_to_nothing = dangle();
}
fn dangle() -> &String {
let s = String::from("hello");
//当代码dangle完成时,s将会被释放。但我们试图返回对它的引用。这意味着该引用将指向无效的String,故此处会报错
&s
}
解决办法如下
fn no_dangle() -> String {
let s = String::from("hello");
s
}
4.4 切片
4.4.1 字符串切片
//切片参数为&str
fn first_word(s: &str) -> &str {
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return &s[0..i];
}
}
&s[..]
}
fn main() {
let my_string = String::from("hello world");
// `first_word` works on slices of `String`s, whether partial or whole
let word = first_word(&my_string[0..6]);
println!("{}",word);
let word = first_word(&my_string[..]);
// `first_word` also works on references to `String`s, which are equivalent
// to whole slices of `String`s
let word = first_word(&my_string);
println!("{}",word);
let my_string_literal = "hello world";
// `first_word` works on slices of string literals, whether partial or whole
let word = first_word(&my_string_literal[0..6]);
println!("{}",word);
let word = first_word(&my_string_literal[..]);
println!("{}",word);
// Because string literals *are* string slices already,
// this works too, without the slice syntax!
let word = first_word(my_string_literal);
println!("{}",word);
}
4.4.2 其他切片
fn main() {
let a = [1, 2, 3, 4, 5];
let slice = &a[1..3];
assert_eq!(slice, &[2, 3]);
}
5.结构体
5.1 定义
#[derive(Debug)]
struct User {
active: bool,
username: String,
email: String,
sign_in_count: u64,
}
fn main() {
//实例化
let mut user1 = User {
active: true,
username: String::from("someusername123"),
email: String::from("someone@example.com"),
sign_in_count: 1,
};
user1.email = String::from("anotheremail@example.com");
println!("{:#?}",user1);
}
//元组方式定义
struct Color(i32, i32, i32);
struct Point(i32, i32, i32);
fn main() {
let black = Color(0, 0, 0);
let origin = Point(0, 0, 0);
}
5.2 方法语法
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
}
impl Rectangle {
fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.height > other.height
}
}
fn main() {
let rect1 = Rectangle {
width: 30,
height: 50,
};
println!(
"The area of the rectangle is {} square pixels.",
rect1.area()
);
let rect2 = Rectangle {
width: 10,
height: 40,
};
let rect3 = Rectangle {
width: 60,
height: 45,
};
println!("Can rect1 hold rect2? {}", rect1.can_hold(&rect2));
println!("Can rect1 hold rect3? {}", rect1.can_hold(&rect3));
}
6.枚举
6.1 定义
#[derive(Debug)]
enum IpAddr {
V4(String),
V6(String),
}
fn main() {
let home = IpAddr::V4(String::from("127.0.0.1"));
let loopback = IpAddr::V6(String::from("::1"));
println!("{:#?}",home)
}
6.2 方法
#[derive(Debug)]
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
impl Message {
fn call(&self) {
// method body would be defined here
println!("{:?}", self);
}
}
fn main() {
let m = Message::Write(String::from("hello"));
m.call();
}
6.3 枚举Option
fn main() {
let x: i8 = 5;
let y: Option<i8> = Some(5);
//let y: Option<i8> = None;
if y.is_none() {
println!("y is None");
return;
}
let sum = x + y.unwrap();
println!("Sum: {}", sum);
}
6.4 match
fn plus_one(x: Option<i32>) -> Option<i32> {
match x {
//None => None,
Some(i) => Some(i + 1),
//需要涵盖所有可能性
_=> None
}
}
fn main() {
let five = Some(5);
let six = plus_one(five);
let none = plus_one(None);
}
6.5 if let
enum Coin {
Penny,
Nickel,
Dime,
Quarter,
}
fn main() {
let mut count = 0;
let coin= Coin::Quarter;
if let Coin::Quarter = coin {
println!("this is quarter!");
} else {
count += 1;
}
}
fn main() {
let config_max = Some(3);
if let Some(max) = config_max {
println!("The maximum is configured to be {max}");
}
}
7.自定义模块
创建模块
cargo new restaurant --lib
执行命令后会生成
└─restaurant
│ Cargo.toml
│
└─src
lib.rs
在lib.rs中写入
pub mod front_of_house {
//仅仅pub修饰的才可被外部调用
pub mod hosting {
pub fn add_to_waitlist() {
println!("excute add_to_waitlist!");
}
fn seat_at_table() {}
}
mod serving {
fn take_order() {}
fn serve_order() {}
fn take_payment() {}
}
}
在最外层main方法中调用
//引入
include!("restaurant/src/lib.rs");
use front_of_house::hosting;
fn main() {
// Relative path
hosting::add_to_waitlist();
}
模块内容分文件存放
模块定义时,如果模块名后边是“;”,而不是代码块:
- Rust会从与模块同名的文件中加载内容
- 模块树的结构不会变化
随着模块逐渐变大,该技术让你可以把模块的内容移动到其它文件中
8.集合
8.1 vector
fn main() {
//使用Vec<T>创建一个新的空Vec<T>实例
let v: Vec<i32> = Vec::new();
//使用vec!宏创建一个包含特定值的Vec<T>实例
let v = vec![1, 2, 3];
//更新Vec<T>的值
let mut v = Vec::new();
v.push(5);
v.push(6);
v.push(7);
v.push(8);
}
fn main() {
let v = vec![1, 2, 3, 4, 5];
//这种方式会导致panic
let does_not_exist = &v[100];
//这种方式会返回None
let does_not_exist = v.get(100);
}
fn main() {
let mut v = vec![1, 2, 3, 4, 5];
let first = &v[0];
//会报错,因为v.push(6)会导致内存重新分配,first指向的内存已经被释放
v.push(6);
println!("The first element is: {first}");
}
fn main() {
let mut v = vec![100, 32, 57];
//遍历
for i in &mut v {
*i += 50;
}
}
fn main() {
enum SpreadsheetCell {
Int(i32),
Float(f64),
Text(String),
}
//使用枚举来储存不同类型的数据
let row = vec![
SpreadsheetCell::Int(3),
SpreadsheetCell::Text(String::from("blue")),
SpreadsheetCell::Float(10.12),
];
}
8.2 HashMap
use std::collections::HashMap;
fn main() {
let mut scores = HashMap::new();
// 插入键值对
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);
let team_name = String::from("Blue");
// 通过get方法获取值,如果存在则返回Some,否则返回None
let score = scores.get(&team_name).copied().unwrap_or(0);
// 通过for循环遍历HashMap
for (key, value) in &scores {
println!("{key}: {value}");
}
//更新值
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Blue"), 25);
// 如果键对应的值不存在,则插入,否则更新
scores.entry(String::from("Blue")).or_insert(50);
}
use std::collections::HashMap;
fn main() {
//统计字符串中单词出现的次数
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:?}");
}
9.错误处理
9.1 立即终止配置
Cargo.toml文件中添加
[profile.release]
panic = 'abort'
9.2 回溯环境变量
RUST_BACKTRACE=1 cargo run
设置RUST_BACKTRACE环境变量来获取导致错误的确切原因的回溯。回溯是为达到这一点而调用的所有函数的列表
9.3 match匹配Result
use std::fs::File;
use std::io::ErrorKind;
fn main() {
let greeting_file_result = File::open("hello.txt");
let greeting_file = match greeting_file_result {
Ok(file) => file,
Err(error) => match error.kind() {
ErrorKind::NotFound => match File::create("hello.txt") {
Ok(fc) => fc,
Err(e) => panic!("Problem creating the file: {e:?}"),
},
other_error => {
panic!("Problem opening the file: {other_error:?}");
}
},
};
}
优化后
use std::fs::File;
use std::io::ErrorKind;
fn main() {
let greeting_file = File::open("hello.txt").unwrap_or_else(|error| {
if error.kind() == ErrorKind::NotFound {
File::create("hello.txt").unwrap_or_else(|error| {
panic!("Problem creating the file: {error:?}");
})
} else {
panic!("Problem opening the file: {error:?}");
}
});
}
9.4 unwrap和expect
use std::fs::File;
fn main() {
let greeting_file = File::open("hello.txt").unwrap();
}
use std::fs::File;
fn main() {
let greeting_file = File::open("hello.txt")
.expect("hello.txt should be included in this project");
}
9.5 函数中错误传播
use std::fs::File;
use std::io::{self, Read};
fn read_username_from_file() -> Result<String, io::Error> {
//使用?返回错误
let mut username_file = File::open("hello.txt")?;
let mut username = String::new();
username_file.read_to_string(&mut username)?;
Ok(username)
}
10.泛型、特征和生命周期
10.1 泛型
10.1.1 函数定义泛型
//PartialOrd is a trait that allows comparison between values of the same type.
fn largest<T: PartialOrd>(list: &[T]) -> &T {
let mut largest = &list[0];
for item in list {
if item > largest {
largest = item;
}
}
largest
}
fn main() {
let number_list = vec![34, 50, 25, 100, 65];
let result = largest(&number_list);
println!("The largest number is {result}");
let char_list = vec!['y', 'm', 'a', 'q'];
let result = largest(&char_list);
println!("The largest char is {result}");
}
10.1.2 结构体中定义泛型
struct Point<T, U> {
x: T,
y: U,
}
fn main() {
let both_integer = Point { x: 5, y: 10 };
let both_float = Point { x: 1.0, y: 4.0 };
let integer_and_float = Point { x: 5, y: 4.0 };
}
struct Point<T> {
x: T,
y: T,
}
impl<T> Point<T> {
fn x(&self) -> &T {
&self.x
}
}
fn main() {
let p = Point { x: 5, y: 10 };
println!("p.x = {}", p.x());
}
10.1.3 枚举中定义泛型
struct Point<X1, Y1> {
x: X1,
y: Y1,
}
impl<X1, Y1> Point<X1, Y1> {
fn mixup<X2, Y2>(self, other: Point<X2, Y2>) -> Point<X1, Y2> {
Point {
x: self.x,
y: other.y,
}
}
}
fn main() {
let p1 = Point { x: 5, y: 10.4 };
let p2 = Point { x: "Hello", y: 'c' };
let p3 = p1.mixup(p2);
println!("p3.x = {}, p3.y = {}", p3.x, p3.y);
}
10.2 特征
在lib.rs中写入
pub trait Summary {
fn summarize(&self) -> String;
}
pub struct NewsArticle {
pub headline: String,
pub location: String,
pub author: String,
pub content: String,
}
// Implementing the Summary trait on the NewsArticle struct
impl Summary for NewsArticle {
fn summarize(&self) -> String {
format!("{}, by {} ({})", self.headline, self.author, self.location)
}
}
pub struct Tweet {
pub username: String,
pub content: String,
pub reply: bool,
pub retweet: bool,
}
// Implementing the Summary trait on the Tweet struct
impl Summary for Tweet {
fn summarize(&self) -> String {
format!("{}: {}", self.username, self.content)
}
}
在main.rs中调用
use hello::{Summary, Tweet};
fn main() {
let tweet = Tweet {
username: String::from("horse_ebooks"),
content: String::from(
"of course, as you probably already know, people",
),
reply: false,
retweet: false,
};
println!("1 new tweet: {}", tweet.summarize());
}
作为参数使用
use std::fmt::{Display, Debug};
fn some_function<T, U>(t: &T, u: &U) -> i32
//use the where keyword to specify that both T must implement Display and Clone trait and U must implement Clone and Debug trait
where
T: Display + Clone,
U: Clone + Debug,
{
1
}
10.3 生命周期
生命周期是我们已经使用过的另一种泛型。生命周期不是确保类型具有我们想要的行为,而是确保引用在我们需要的时间内有效。
//'a is a lifetime annotation
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
fn main() {
let string1 = String::from("abcd");
let string2 = "xyz";
let result = longest(string1.as_str(), string2);
println!("The longest string is {result}");
}
// 'static is a lifetime annotation, which means that this reference can live for the entire duration of the program.
let s: &'static str = "I have a static lifetime.";
11.自动化测试
$ cargo new adder --lib
Created library `adder` project
$ cd adder
在src/lib.rs中会自动生成
pub fn add(left: usize, right: usize) -> usize {
left + right
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
let result = add(2, 2);
assert_eq!(result, 4);
}
}
执行cargo test命令可进行测试
12.命令行程序案例
需求:输入命令 xx.exe query filename从文件中查找包含query的文本,并打印出来
lib.rs
use std::env;
pub struct Args {
filename: String,
query: String,
case_sensitive: bool
}
impl Args {
pub fn new(args: &[String]) -> Result<Args, &'static str> {
if args.len() < 3 {
return Err("not enough arguments");
}
let filename = args[2].clone();
let query = args[1].clone();
//获取环境变量
let case_sensitive = env::var("CASE_SENSITIVE").is_ok();
Ok(Args { filename, query , case_sensitive: case_sensitive})
}
}
pub fn run(config: Args) -> Result<(), std::io::Error> {
let contents = std::fs::read_to_string(config.filename)?;
if config.case_sensitive {
search(&config.query, &contents);
} else {
search_case_insensitive(&config.query, &contents);
}
Ok(())
}
pub fn search(query: &str, contents: &str) {
//filter returns an iterator
let result: Vec<String> = contents.lines().filter(|line| line.contains(query)).map(|s| s.to_string()).collect();
println!("{:?}", result);
}
pub fn search_case_insensitive(query: &str, contents: &str) {
let query = query.to_lowercase();
let result: Vec<String> = contents.lines().filter(|line| line.to_lowercase().contains(&query)).map(|s| s.to_string()).collect();
println!("{:?}", result);
}
main.rs
use std::env;
use hello::{run, Args};
fn main() {
let args:Vec<String> = env::args().collect();
let args = Args::new(&args).unwrap_or_else(|err| {
eprintln!("Problem parsing arguments: {}", err);
std::process::exit(1);
});
if let Err(e) = run(args){
eprintln!("Application error: {}", e);
std::process::exit(1);
};
}
13.闭包
13.1 定义闭包
fn main() {
let mut list = vec![1, 2, 3];
println!("Before defining closure: {list:?}");
//相当于定义一个函数
let mut borrows_mutably = || list.push(7);
//执行
borrows_mutably();
println!("After calling closure: {list:?}");
}
13.2 转让所有权给闭包
use std::thread;
fn main() {
let list = vec![1, 2, 3];
println!("Before defining closure: {list:?}");
//move关键字表示将list的所有权转移给闭包
thread::spawn(move || println!("From thread: {list:?}"))
.join()
.unwrap();
}
13.3 排序
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
fn main() {
let mut list = [
Rectangle { width: 10, height: 1 },
Rectangle { width: 3, height: 5 },
Rectangle { width: 7, height: 12 },
];
let mut num_sort_operations = 0;
//sort_by_key is a method of the Iterator trait
list.sort_by_key(|r| {
num_sort_operations += 1;
r.width
});
println!("{list:#?}, sorted in {num_sort_operations} operations");
}
13.4 迭代器
fn main() {
let v1: Vec<i32> = vec![1, 2, 3];
//map returns an iterator
let v2: Vec<_> = v1.iter().map(|x| x + 1).collect();
assert_eq!(v2, vec![2, 3, 4]);
println!("{:?}", v2);
}
14.智能指针
14.1 使用Box在堆上存储数据
fn main() {
let b = Box::new(5);
println!("b = {b}");
}
14.2 自定义智能指针
struct MyBox<T>(T);
use std::ops::Deref;
impl<T> Deref for MyBox<T> {
type Target = T;
// deref 方法返回一个引用,这样 * 运算符就可以使用在 MyBox<T> 值上了
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> MyBox<T> {
fn new(x: T) -> MyBox<T> {
MyBox(x)
}
}
fn main() {
let x = 5;
let y = MyBox::new(x);
assert_eq!(5, x);
assert_eq!(5, *y);
}
14.3 drop使用
struct CustomSmartPointer {
data: String,
}
impl Drop for CustomSmartPointer {
// Drop trait is used to run some code when a value goes out of scope
fn drop(&mut self) {
println!("Dropping CustomSmartPointer with data `{}`!", self.data);
}
}
fn main() {
let c = CustomSmartPointer {
data: String::from("my stuff"),
};
let d = CustomSmartPointer {
data: String::from("other stuff"),
};
println!("CustomSmartPointers created.");
}
14.4 引用计数智能指针
enum List {
Cons(i32, Rc<List>),
Nil,
}
use crate::List::{Cons, Nil};
use std::rc::Rc;
fn main() {
let a = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil)))));
//统计引用计数,结果为1
println!("count after creating a = {}", Rc::strong_count(&a));
let b = Cons(3, Rc::clone(&a));
//统计引用计数,结果为2
println!("count after creating b = {}", Rc::strong_count(&a));
{
let c = Cons(4, Rc::clone(&a));
//统计引用计数,结果为3
println!("count after creating c = {}", Rc::strong_count(&a));
}
//统计引用计数,结果为2
println!("count after c goes out of scope = {}", Rc::strong_count(&a));
}
14.5 内部可变指针
pub trait Messenger {
fn send(&self, msg: &str);
}
//定义触发器结构体
pub struct LimitTracker<'a, T: Messenger> {
messenger: &'a T,
value: usize,
max: usize,
}
impl<'a, T> LimitTracker<'a, T>
where
T: Messenger,
{
pub fn new(messenger: &'a T, max: usize) -> LimitTracker<'a, T> {
LimitTracker {
messenger,
value: 0,
max,
}
}
//设置触发器的值
pub fn set_value(&mut self, value: usize) {
self.value = value;
let percentage_of_max = self.value as f64 / self.max as f64;
if percentage_of_max >= 1.0 {
self.messenger.send("Error: You are over your quota!");
} else if percentage_of_max >= 0.9 {
self.messenger
.send("Urgent warning: You've used up over 90% of your quota!");
} else if percentage_of_max >= 0.75 {
self.messenger
.send("Warning: You've used up over 75% of your quota!");
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::cell::RefCell;
//定义消息结构体
struct MockMessenger {
//RefCell<T>是一个智能指针,它允许在运行时而不是在编译时检查借用规则
sent_messages: RefCell<Vec<String>>,
}
impl MockMessenger {
fn new() -> MockMessenger {
MockMessenger {
sent_messages: RefCell::new(vec![]),
}
}
}
impl Messenger for MockMessenger {
//发送消息
fn send(&self, message: &str) {
//RefCell<T>的borrow_mut方法返回RefMut<T>智能指针类型,它记录了运行时借用检查
self.sent_messages.borrow_mut().push(String::from(message));
println!("{}", message);
}
}
#[test]
fn it_sends_an_over_75_percent_warning_message() {
let mock_messenger = MockMessenger::new();
let mut limit_tracker = LimitTracker::new(&mock_messenger, 100);
limit_tracker.set_value(80);
assert_eq!(mock_messenger.sent_messages.borrow().len(), 1);
}
}
14.6 使用弱引用解决循环引用导致的内存泄漏问题
use std::cell::RefCell;
use std::rc::{Rc, Weak};
#[derive(Debug)]
struct Node {
value: i32,
//parent字段是一个可变弱引用,用于指向父节点
parent: RefCell<Weak<Node>>,
children: RefCell<Vec<Rc<Node>>>,
}
fn main() {
let leaf = Rc::new(Node {
value: 3,
//初始化一个Weak指针,指向一个空的Node
parent: RefCell::new(Weak::new()),
children: RefCell::new(vec![]),
});
println!(
"leaf strong = {}, weak = {}",
//结果是1和0,因为leaf是唯一的Rc指针,没有其他指针指向它
Rc::strong_count(&leaf),
Rc::weak_count(&leaf),
);
{
let branch = Rc::new(Node {
value: 5,
parent: RefCell::new(Weak::new()),
children: RefCell::new(vec![Rc::clone(&leaf)]),
});
*leaf.parent.borrow_mut() = Rc::downgrade(&branch);
println!(
"branch strong = {}, weak = {}",
//结果是1和1,因为branch是唯一的Rc指针,但有一个Weak指针指向它
Rc::strong_count(&branch),
Rc::weak_count(&branch),
);
println!(
"leaf strong = {}, weak = {}",
//结果是2和0,因为leaf有两个Rc指针指向它
Rc::strong_count(&leaf),
Rc::weak_count(&leaf),
);
}
//branch离开作用域后,leaf的唯一Rc指针被销毁
println!("leaf parent = {:?}", leaf.parent.borrow().upgrade());
println!(
"leaf strong = {}, weak = {}",
//结果是1和0,因为branch离开作用域后,leaf的唯一Rc指针被销毁
Rc::strong_count(&leaf),
Rc::weak_count(&leaf),
);
}
15.并发
15.1 多线程
use std::thread;
use std::time::Duration;
fn main() {
// Creating a new thread
let handle = thread::spawn(|| {
for i in 1..10 {
println!("hi number {i} from the spawned thread!");
thread::sleep(Duration::from_millis(1));
}
});
for i in 1..5 {
println!("hi number {i} from the main thread!");
thread::sleep(Duration::from_millis(1));
}
// Wait for the spawned thread to finish
handle.join().unwrap();
}
15.2 与move一起使用
use std::thread;
fn main() {
let v = vec![1, 2, 3];
//move closure takes ownership of v
let handle = thread::spawn(move || {
println!("Here's a vector: {v:?}");
});
handle.join().unwrap();
}
15.3 线程间数据共享
use std::sync::mpsc;
use std::thread;
fn main() {
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
let val = String::from("hi");
//执行send方法后,val的所有权被转移给了接收者,所以这里不能再使用val
tx.send(val).unwrap();
//println!("val is {val}");
});
let received = rx.recv().unwrap();
println!("Got: {received}");
}
use std::sync::mpsc;
use std::thread;
use std::time::Duration;
//多个消息发送者
fn main() {
let (tx, rx) = mpsc::channel();
// Cloning the transmitter to send it to another thread
let tx1 = tx.clone();
thread::spawn(move || {
let vals = vec![
String::from("hi"),
String::from("from"),
String::from("the"),
String::from("thread"),
];
for val in vals {
tx1.send(val).unwrap();
thread::sleep(Duration::from_secs(1));
}
});
thread::spawn(move || {
let vals = vec![
String::from("more"),
String::from("messages"),
String::from("for"),
String::from("you"),
];
for val in vals {
tx.send(val).unwrap();
thread::sleep(Duration::from_secs(1));
}
});
for received in rx {
println!("Got: {received}");
}
}
15.4 线程间状态共享
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
//Arc是一个原子引用计数类型,可以安全的在线程间共享数据,mutex是一个互斥锁,可以保证在同一时刻只有一个线程访问数据
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..10 {
let counter = Arc::clone(&counter);
let handle = thread::spawn(move || {
let mut num = counter.lock().unwrap();
*num += 1;
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
println!("Result: {}", *counter.lock().unwrap());
}
16.模式和匹配
16.1 if let
fn main() {
let favorite_color: Option<&str> = None;
let is_tuesday = false;
let age: Result<u8, _> = "34".parse();
if let Some(color) = favorite_color {
println!("Using your favorite color, {color}, as the background");
} else if is_tuesday {
println!("Tuesday is green day!");
} else if let Ok(age) = age {
//这里会执行两次匹配,第一次匹配age是否是Ok,第二次匹配age的值是否大于30,结果是age的值是34,所以会输出Using purple as the background color
if age > 30 {
println!("Using purple as the background color");
} else {
println!("Using orange as the background color");
}
} else {
println!("Using blue as the background color");
}
}
16.2 while let
fn main() {
let mut stack = Vec::new();
stack.push(1);
stack.push(2);
stack.push(3);
//结果是3 2 1
while let Some(top) = stack.pop() {
println!("{top}");
}
}
16.3 if let
fn main() {
let v = vec!['a', 'b', 'c'];
for (index, value) in v.iter().enumerate() {
println!("{value} is at index {index}");
}
}
16.4 可反驳性
函数参数、let语句和for循环只能接受无可辩驳的模式,因为当值不匹配时程序无法执行任何有意义的操作。if letand表达式while let接受可反驳和不可反驳的模式,但编译器会警告不可反驳的模式,因为根据定义,它们旨在处理可能的失败:条件的功能在于其能够根据成功或失败执行不同的操作。
16.5 模式语法
16.5.1 配置值
fn main() {
let x = 1;
match x {
1 => println!("one"),
2 => println!("two"),
3 => println!("three"),
_ => println!("anything"),
}
}
16.5.2 匹配命名变量
fn main() {
let x = Some(5);
let y = 10;
match x {
Some(50) => println!("Got 50"),
//匹配到Some(y)时,将y的值绑定到y变量
Some(y) => println!("Matched, y = {y}"),
_ => println!("Default case, x = {x:?}"),
}
println!("at the end: x = {x:?}, y = {y}");
//Matched, y = 5
//at the end: x = Some(5), y = 10
}
16.5.3 多种模式
fn main() {
let x = 1;
match x {
1 | 2 => println!("one or two"),
3 => println!("three"),
_ => println!("anything"),
}
}
16.5.4 匹配返回
fn main() {
let x = 5;
match x {
// 1..=5 is a range pattern that matches any number from 1 through 5
1..=5 => println!("one through five"),
_ => println!("something else"),
}
}
16.5.5 解构
struct Point {
x: i32,
y: i32,
}
fn main() {
let p = Point { x: 0, y: 7 };
let Point { x: a, y: b } = p;
assert_eq!(0, a);
assert_eq!(7, b);
}
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
fn main() {
let msg = Message::ChangeColor(0, 160, 255);
match msg {
Message::Quit => {
println!("The Quit variant has no data to destructure.");
}
Message::Move { x, y } => {
println!("Move in the x direction {x} and in the y direction {y}");
}
Message::Write(text) => {
println!("Text message: {text}");
}
Message::ChangeColor(r, g, b) => {
println!("Change the color to red {r}, green {g}, and blue {b}")
}
}
}
16.5.6 忽略值
fn foo(_: i32, y: i32) {
println!("This code only uses the y parameter: {y}");
}
fn main() {
foo(3, 4);
}
fn main() {
struct Point {
x: i32,
y: i32,
z: i32,
}
let origin = Point { x: 0, y: 0, z: 0 };
match origin {
//忽略剩余的字段
Point { x, .. } => println!("x is {x}"),
}
}
16.5.7 match额外条件
fn main() {
let num = Some(4);
match num {
//匹配Some中的值,如果是偶数则打印
Some(x) if x % 2 == 0 => println!("The number {x} is even"),
Some(x) => println!("The number {x} is odd"),
None => (),
}
}
17.高级特性
17.1 原始指针
fn main() {
let mut num = 5;
//创建原始指针
let r1 = &num as *const i32;
let r2 = &mut num as *mut i32;
println!("r1: {:p}", r1);
println!("r1: {:p}", r2);
}
17.2 不安全代码块中执行不安全方法
fn main() {
unsafe fn dangerous() {println!("dangerous function");}
unsafe {
dangerous();
}
}
use std::slice;
fn split_at_mut(values: &mut [i32], mid: usize) -> (&mut [i32], &mut [i32]) {
let len = values.len();
let ptr = values.as_mut_ptr();
assert!(mid <= len);
// The slice::from_raw_parts_mut function is unsafe because it can create aliased mutable references.
unsafe {
(
// slice::from_raw_parts_mut(ptr, mid) is equivalent to &mut values[..mid]
slice::from_raw_parts_mut(ptr, mid),
// slice::from_raw_parts_mut(ptr.add(mid), len - mid) is equivalent to &mut values[mid..]
slice::from_raw_parts_mut(ptr.add(mid), len - mid),
)
}
}
fn main() {
let mut v = vec![1, 2, 3, 4, 5, 6];
let r = &mut v[..];
let (a, b) = split_at_mut(r,3);
assert_eq!(a, &mut [1, 2, 3]);
assert_eq!(b, &mut [4, 5, 6]);
}
17.3 使用extern函数调用外部代码
extern "C" {
// This is the C function that we want to call
fn abs(input: i32) -> i32;
}
fn main() {
unsafe {
// Call the C function
println!("Absolute value of -3 according to C: {}", abs(-3));
}
}
17.4 C语言调用rust
#[no_mangle]
pub extern "C" fn call_from_c() {
println!("Just called a Rust function from C!");
}
17.5 修改静态变量
static mut COUNTER: u32 = 0;
fn add_to_count(inc: u32) {
unsafe {
COUNTER += inc;
}
}
fn main() {
add_to_count(3);
unsafe {
println!("COUNTER: {COUNTER}");
}
}
17.6 不安全trait
//定义一个 unsafe trait
unsafe trait Foo {
fn foo(&self);
}
unsafe impl Foo for i32 {
//实现foo方法
fn foo(&self) {
println!("Hello, world!");
}
}
fn main() {
let x = 42;
//在unsafe块中调用foo方法
unsafe {
x.foo();
}
}
17.7 默认运算符重载
use std::ops::Add;
#[derive(Debug, Copy, Clone, PartialEq)]
struct Point {
x: i32,
y: i32,
}
impl Add for Point {
type Output = Point;
fn add(self, other: Point) -> Point {
Point {
x: self.x + other.x,
y: self.y + other.y,
}
}
}
fn main() {
assert_eq!(
Point { x: 1, y: 0 } + Point { x: 2, y: 3 },
Point { x: 3, y: 3 }
);
}
use std::ops::Add;
struct Millimeters(u32);
struct Meters(u32);
impl Add<Meters> for Millimeters {
type Output = Millimeters;
fn add(self, other: Meters) -> Millimeters {
Millimeters(self.0 + (other.0 * 1000))
}
}
fn main() {
let m = Millimeters(10) + Meters(1);
println!("{}", m.0);
}
17.8 调用同名方法
trait Animal {
fn baby_name() -> String;
}
struct Dog;
impl Dog {
fn baby_name() -> String {
String::from("Spot")
}
}
impl Animal for Dog {
fn baby_name() -> String {
String::from("puppy")
}
}
fn main() {
//调用Dog的baby_name方法
println!("A baby dog is called a {}", Dog::baby_name());
//调用Animal的baby_name方法
print!("A baby dog is called a {}", <Dog as Animal>::baby_name());
}
17.9 trait继承
use std::fmt;
//该trait继承了fmt::Display,所以我们可以使用to_string方法
trait OutlinePrint: fmt::Display {
fn outline_print(&self) {
let output = self.to_string();
let len = output.len();
println!("{}", "*".repeat(len + 4));
println!("*{}*", " ".repeat(len + 2));
println!("* {output} *");
println!("*{}*", " ".repeat(len + 2));
println!("{}", "*".repeat(len + 4));
}
}
struct Point {
x: i32,
y: i32,
}
impl OutlinePrint for Point {}
//必须实现fmt::Display
impl fmt::Display for Point {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "({}, {})", self.x, self.y)
}
}
fn main() {
let p = Point { x: 1, y: 2 };
p.outline_print();
}
17.10 包装
//这是一个包装器类型,它包含一个 Vec<String>。我们想要实现 fmt::Display 来打印这个包装器类型,但是 Vec<T> 并不实现 fmt::Display。因此,Vec<T> 也不满足打印的要求。
struct Wrapper(Vec<String>);
impl fmt::Display for Wrapper {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "[{}]", self.0.join(", "))
}
}
fn main() {
let w = Wrapper(vec![String::from("hello"), String::from("world")]);
println!("w = {w}");
}
17.11 类型别名
fn main() {
//类型别名
type Kilometers = i32;
let x: i32 = 5;
let y: Kilometers = 5;
println!("x + y = {}", x + y);
}
fn main() {
//定义类型别名来减少重复
type Thunk = Box<dyn Fn() + Send + 'static>;
let f: Thunk = Box::new(|| println!("hi"));
//使用类型别名
fn takes_long_type(f: Thunk) {
// --snip--
}
fn returns_long_type() -> Thunk {
Box::new(|| println!("hi"))
}
}
17.12 永不反回类型
Rust 有一种特殊类型!,在类型理论术语中称为 空类型,因为它没有值。我们更喜欢将其称为never 类型 ,因为当函数永远不会返回时,它代表返回类型。
fn bar() -> ! {
// --snip--
}
17.13 函数指针
fn add_one(x: i32) -> i32 {
x + 1
}
//参数是一个函数
fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 {
f(arg) + f(arg)
}
fn main() {
let answer = do_twice(add_one, 5);
println!("The answer is: {answer}");
}
fn main() {
let list_of_numbers = vec![1, 2, 3];
//传入一个闭包
let list_of_strings: Vec<String> =
list_of_numbers.iter().map(|i| i.to_string()).collect();
println!("{:?}", list_of_strings);
let list_of_numbers = vec![1, 2, 3];
//传入一个函数
let list_of_strings: Vec<String> =
list_of_numbers.iter().map(ToString::to_string).collect();
println!("{:?}", list_of_strings);
}
17.14 返回特征类型
//返回特征对象,不能直接返回闭包,因为闭包类型是匿名的
fn returns_closure() -> Box<dyn Fn(i32) -> i32> {
Box::new(|x| x + 1)
}
fn main() {
let closure = returns_closure();
println!("{}", closure(5));
}
18.多线程web服务器案例
18.1 定义线程池和线程
//lib.rs
use std::{
sync::{mpsc, Arc, Mutex},
thread,
};
pub struct ThreadPool {
//定义一个Worker结构体的数组
workers: Vec<Worker>,
//定义一个Option类型的mpsc::Sender<Job>,用于发送Job
sender: Option<mpsc::Sender<Job>>,
}
type Job = Box<dyn FnOnce() + Send + 'static>;
impl ThreadPool {
/// Create a new ThreadPool.
///
/// The size is the number of threads in the pool.
///
/// # Panics
///
/// The `new` function will panic if the size is zero.
pub fn new(size: usize) -> ThreadPool {
assert!(size > 0);
let (sender, receiver) = mpsc::channel();
let receiver = Arc::new(Mutex::new(receiver));
let mut workers = Vec::with_capacity(size);
//创建size个Worker结构体,并将其放入workers数组中
for id in 0..size {
workers.push(Worker::new(id, Arc::clone(&receiver)));
}
ThreadPool {
workers,
sender: Some(sender),
}
}
pub fn execute<F>(&self, f: F)
where
F: FnOnce() + Send + 'static,
{
let job = Box::new(f);
//将job发送给Worker
self.sender.as_ref().unwrap().send(job).unwrap();
}
}
impl Drop for ThreadPool {
fn drop(&mut self) {
drop(self.sender.take());
for worker in &mut self.workers {
println!("Shutting down worker {}", worker.id);
if let Some(thread) = worker.thread.take() {
thread.join().unwrap();
}
}
}
}
struct Worker {
id: usize,
thread: Option<thread::JoinHandle<()>>,
}
impl Worker {
fn new(id: usize, receiver: Arc<Mutex<mpsc::Receiver<Job>>>) -> Worker {
//创建一个新的线程,用于接收Job
let thread = thread::spawn(move || loop {
let message = receiver.lock().unwrap().recv();
match message {
Ok(job) => {
println!("Worker {id} got a job; executing.");
job();
}
Err(_) => {
println!("Worker {id} disconnected; shutting down.");
break;
}
}
});
Worker {
id,
thread: Some(thread),
}
}
}
18.2 创建返回的html
hello.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Hello!</title>
</head>
<body>
<h1>Hello!</h1>
<p>Hi from Rust</p>
</body>
</html>
404.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Hello!</title>
</head>
<body>
<h1>Oops!</h1>
<p>Sorry, I don't know what you're asking for.</p>
</body>
</html>
18.3 在main方法中实现web服务器
use hello::ThreadPool;
use std::{
fs,
io::{prelude::*, BufReader},
net::{TcpListener, TcpStream},
thread,
time::Duration,
};
fn main() {
let listener = TcpListener::bind("127.0.0.1:7878").unwrap();
//创建一个线程池,线程数为4
let pool = ThreadPool::new(4);
for stream in listener.incoming().take(2) {
let stream = stream.unwrap();
//将stream放入线程池中
pool.execute(|| {
handle_connection(stream);
});
}
println!("Shutting down.");
}
fn handle_connection(mut stream: TcpStream) {
let buf_reader = BufReader::new(&stream);
let request_line = buf_reader.lines().next().unwrap().unwrap();
let (status_line, filename) = match &request_line[..] {
"GET / HTTP/1.1" => ("HTTP/1.1 200 OK", "hello.html"),
"GET /sleep HTTP/1.1" => {
thread::sleep(Duration::from_secs(5));
("HTTP/1.1 200 OK", "hello.html")
}
_ => ("HTTP/1.1 404 NOT FOUND", "404.html"),
};
let contents = fs::read_to_string(filename).unwrap();
let length = contents.len();
let response =
format!("{status_line}\r\nContent-Length: {length}\r\n\r\n{contents}");
stream.write_all(response.as_bytes()).unwrap();
}