├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── README.md ├── bytes ├── .gitignore ├── Cargo.lock ├── Cargo.toml └── src │ └── main.rs ├── c调用rust ├── .gitignore ├── Cargo.toml └── src │ ├── lib.rs │ └── main.c ├── hello_macro ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── hello_macro_derive │ ├── .gitignore │ ├── Cargo.toml │ └── src │ │ └── lib.rs └── src │ ├── lib.rs │ └── main.rs ├── linked_list ├── .gitignore ├── Cargo.lock ├── Cargo.toml └── src │ └── bin │ ├── double_unsafe.rs │ ├── single.rs │ └── single2.rs ├── minigrep ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── poem.txt ├── src │ ├── lib.rs │ └── main.rs └── tests │ └── search.rs ├── rust_golang_ffi_demo ├── golang │ ├── cgo │ │ └── rustdemo.h │ ├── go.mod │ ├── main.go │ └── mainc └── rustdemo │ ├── .gitignore │ ├── Cargo.toml │ └── src │ └── lib.rs ├── rust调用c ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── build.rs └── src │ ├── double.c │ ├── main.rs │ └── third.c ├── sayhello_lib ├── .gitignore ├── Cargo.toml └── src │ └── lib.rs ├── src └── main.rs ├── study_test ├── .gitignore ├── Cargo.toml ├── benches │ └── my_benchmark.rs ├── src │ └── lib.rs └── tests │ └── add_test.rs ├── timer_future ├── .gitignore ├── Cargo.toml └── src │ └── lib.rs ├── unsafe_rust ├── .gitignore ├── Cargo.lock ├── Cargo.toml └── src │ ├── libadd.so │ └── main.rs ├── web服务器 ├── 单线程版本的_web_服务器 │ ├── .gitignore │ ├── 404.html │ ├── Cargo.lock │ ├── Cargo.toml │ ├── hello.html │ └── src │ │ └── main.rs ├── 实践多线程_web服务器 │ ├── .gitignore │ ├── 404.html │ ├── Cargo.lock │ ├── Cargo.toml │ ├── hello.html │ └── src │ │ ├── lib.rs │ │ └── main.rs ├── 异步_web服务器 │ ├── .gitignore │ ├── 404.html │ ├── Cargo.lock │ ├── Cargo.toml │ ├── hello.html │ └── src │ │ └── main.rs └── 异步多线程_web服务器 │ ├── .gitignore │ ├── 404.html │ ├── Cargo.lock │ ├── Cargo.toml │ ├── hello.html │ └── src │ └── main.rs ├── world_hello ├── .gitignore ├── Cargo.lock ├── Cargo.toml └── src │ └── main.rs ├── world_hello2 ├── .gitignore ├── Cargo.lock ├── Cargo.toml └── src │ └── main.rs ├── 基本数据类型 ├── .gitignore ├── Cargo.lock ├── Cargo.toml └── src │ └── main.rs ├── 复合类型 ├── .gitignore ├── Cargo.lock ├── Cargo.toml └── src │ ├── array.rs │ ├── collection.rs │ ├── enumeration.rs │ ├── main.rs │ ├── sort.rs │ ├── struct1.rs │ └── tuple.rs ├── 宏编程 ├── .gitignore ├── Cargo.lock ├── Cargo.toml └── src │ └── main.rs ├── 实现一个简单的redis ├── hello-my-redis │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ ├── examples │ │ └── hello-redis.rs │ └── src │ │ └── main.rs ├── mini_redis │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── my-redis-stream │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ └── src │ │ └── bin │ │ └── client.rs ├── my-redis-共享状态 │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ ├── examples │ │ └── hello-redis.rs │ └── src │ │ └── bin │ │ ├── client.rs │ │ └── server.rs ├── tokio_hello │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ ├── examples │ │ ├── main1.rs │ │ ├── main2.rs │ │ ├── main3.rs │ │ ├── main4.rs │ │ ├── main5.rs │ │ └── main6.rs │ └── src │ │ └── main.rs ├── tokio_select │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── tokio_stream │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── tokio_tcp │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ └── src │ │ └── bin │ │ ├── client.rs │ │ └── server.rs ├── tokio_优雅的关闭 │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── tokio_协作式调度 │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ ├── examples │ │ └── example1.rs │ └── src │ │ └── main.rs ├── tokio_异步原理 │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ └── src │ │ └── main.rs └── tokio_异步跟同步共存 │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ └── src │ ├── main.rs │ └── other.rs ├── 并发编程 ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── examples │ ├── alternate.go │ ├── alternate1.rs │ ├── alternate2.rs │ ├── alternate3.rs │ ├── barrier.rs │ ├── deadlock.rs │ └── main5.rs └── src │ ├── config.rs │ └── main.rs ├── 异步编程 ├── .gitignore ├── Cargo.lock ├── Cargo.toml └── src │ └── main.rs ├── 循环引用与自引用 ├── .gitignore ├── Cargo.lock ├── Cargo.toml └── src │ └── main.rs ├── 所有权和借用 ├── .gitignore ├── Cargo.lock ├── Cargo.toml └── src │ ├── main.rs │ └── study.rs ├── 文件操作 ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── hello.txt ├── lorem_ipsum.txt └── src │ ├── hello.txt │ └── main.rs ├── 方法_泛型_特征 ├── .gitignore ├── Cargo.lock ├── Cargo.toml └── src │ ├── closure.rs │ ├── generic.rs │ ├── inheritance.rs │ ├── main.rs │ ├── operator_overloading.rs │ ├── polymorphism.rs │ ├── rust_type.rs │ └── trait_study.rs ├── 日志与监控 ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── examples │ ├── main1.rs │ ├── main2.rs │ ├── main3.rs │ ├── main4.rs │ └── main5.rs └── src │ └── main.rs ├── 智能指针 ├── .gitignore ├── Cargo.lock ├── Cargo.toml └── src │ ├── cow.rs │ └── main.rs ├── 构建脚本 ├── hello │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ ├── build.rs │ └── src │ │ └── main.rs ├── 条件编译 │ ├── .gitignore │ ├── Cargo.toml │ ├── build.rs │ └── src │ │ └── main.rs └── 链接系统库 │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ ├── build.rs │ └── src │ └── lib.rs ├── 模式匹配 ├── .gitignore ├── Cargo.lock ├── Cargo.toml └── src │ └── main.rs ├── 流程控制 ├── .gitignore ├── Cargo.lock ├── Cargo.toml └── src │ └── main.rs ├── 猜数游戏 ├── .gitignore ├── Cargo.lock ├── Cargo.toml └── src │ └── main.rs ├── 生命周期 ├── .gitignore ├── Cargo.lock ├── Cargo.toml └── src │ ├── advance.rs │ └── main.rs ├── 配置文件 ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── examples │ ├── main1.rs │ ├── main2.rs │ ├── main3.rs │ ├── main4.rs │ └── main5.rs └── src │ └── main.rs └── 错误处理与格式化输出 ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── hello.txt ├── hello2.txt └── src ├── error_handle.rs ├── format.rs └── main.rs /.gitignore: -------------------------------------------------------------------------------- 1 | *.exe 2 | *.pdb 3 | *.vscode 4 | .vscode 5 | *.lib 6 | *.dll 7 | mytest/* 8 | .idea 9 | /target 10 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "learning_materials" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | [dependencies] 7 | 8 | 9 | [workspace] 10 | members = [ 11 | "bytes", 12 | "c调用rust", 13 | "hello_macro", 14 | "linked_list", 15 | "minigrep", 16 | "rust_golang_ffi_demo/rustdemo", 17 | "rust调用c", 18 | "sayhello_lib", 19 | "study_test", 20 | "timer_future", 21 | "unsafe_rust", 22 | "world_hello", 23 | "world_hello2", 24 | "基本数据类型", 25 | "复合类型", 26 | "宏编程", 27 | "并发编程", 28 | "异步编程", 29 | "循环引用与自引用", 30 | "所有权和借用", 31 | "文件操作", 32 | "方法_泛型_特征", 33 | "日志与监控", 34 | "智能指针", 35 | # "构建脚本/链接系统库", 36 | # "构建脚本/hello", 37 | # "构建脚本/条件编译", 38 | # "mytest", 39 | "模式匹配", 40 | "流程控制", 41 | "猜数游戏", 42 | "生命周期", 43 | "配置文件", 44 | "错误处理与格式化输出", 45 | "实现一个简单的redis/hello-my-redis", 46 | "实现一个简单的redis/mini_redis", 47 | "实现一个简单的redis/my-redis-共享状态", 48 | "实现一个简单的redis/my-redis-stream", 49 | "实现一个简单的redis/tokio_协作式调度", 50 | "实现一个简单的redis/tokio_异步跟同步共存", 51 | "实现一个简单的redis/tokio_异步原理", 52 | "实现一个简单的redis/tokio_优雅的关闭", 53 | "实现一个简单的redis/tokio_hello", 54 | "实现一个简单的redis/tokio_select", 55 | "实现一个简单的redis/tokio_stream", 56 | "实现一个简单的redis/tokio_tcp", 57 | "web服务器/单线程版本的_web_服务器", 58 | "web服务器/实践多线程_web服务器", 59 | "web服务器/异步_web服务器", 60 | "web服务器/异步多线程_web服务器", 61 | ] 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Rust Code Snippets 4 | 5 | Rust语言圣经代码片段合集,带知识点注释与更多扩展。 6 | 7 | 8 | 9 | 自己用来快速搜索回顾相关语法与知识点。 10 | 11 | 12 | 食用指南:跟着程序执行的流程学习。 13 | 14 | 15 | ## 相关链接 16 | 17 | - [Rust语言圣经](https://course.rs/) 18 | - [通过例子学 Rust 中文版](https://rustwiki.org/zh-CN/rust-by-example/) 19 | - [Rust 程序设计语言 中文版](https://rustwiki.org/zh-CN/book/) 20 | - [Rust 官方文档中文教程](https://rustwiki.org/) 21 | - [Rust 参考手册 中文版](https://rustwiki.org/zh-CN/reference/) 22 | - [Rust Cookbook 中文版](https://rustwiki.org/zh-CN/rust-cookbook/) 23 | - [Rust 标准库 中文文档](https://rustwiki.org/zh-CN/std/) -------------------------------------------------------------------------------- /bytes/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /bytes/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "bytes" 7 | version = "1.4.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" 10 | 11 | [[package]] 12 | name = "stu_bytes" 13 | version = "0.1.0" 14 | dependencies = [ 15 | "bytes", 16 | ] 17 | -------------------------------------------------------------------------------- /bytes/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "stu_bytes" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | bytes = "1" 10 | -------------------------------------------------------------------------------- /bytes/src/main.rs: -------------------------------------------------------------------------------- 1 | use bytes::{Buf, BufMut}; 2 | use bytes::{Bytes, BytesMut}; 3 | 4 | fn main() { 5 | // 使用 Vec 来保存目标数据的时候,有一个问题, 6 | // 对它进行克隆时会将底层数据也整个复制一份,效率很低 7 | // Bytes 是一个引用计数类型,跟 Arc 非常类似,或者准确的说, 8 | // Bytes 就是基于 Arc 实现的,但相比后者Bytes 提供了一些额外的能力。 9 | 10 | println!("-----------------Bytes-----------------"); 11 | // example1 12 | let mut mem = Bytes::from("Hello world"); 13 | let a = mem.slice(0..5); 14 | 15 | assert_eq!(a, "Hello"); 16 | 17 | // 一分为二,返回前半部分,后半部分保留在mem中(底层数据没有复制,仅改变了指针) 18 | // this is an O(1) operation that just increases the reference count and sets a few indices. 19 | let b = mem.split_to(6); 20 | 21 | assert_eq!(mem, "world"); 22 | assert_eq!(b, "Hello "); 23 | 24 | println!("-----------------BytesMut-----------------"); 25 | // BytesMut 是 Bytes 的可变版本 26 | 27 | // example1 28 | // 创建一个容量为1024字节的 BytesMut,超过这个容量时会自动扩容 29 | let mut buf = BytesMut::with_capacity(1024); 30 | buf.put(&b"hello world"[..]); 31 | assert_eq!(11, buf.len()); 32 | assert_eq!(1024, buf.capacity()); 33 | 34 | // 清空buf,并返回一个新的BytesMut(底层数据没有复制,仅改变了指针) 35 | // This is an O(1) operation that just increases the reference count and sets a few indices. 36 | let other = buf.split(); 37 | 38 | assert!(buf.is_empty()); 39 | assert_eq!(1013, buf.capacity()); // 1024 - 11 = 1013 40 | 41 | assert_eq!(other, b"hello world"[..]); 42 | assert_eq!(11, other.capacity()); 43 | 44 | // example2 45 | let mut buf = BytesMut::with_capacity(64); 46 | buf.put_u8(b'h'); 47 | buf.put_u8(b'e'); 48 | buf.put(&b"llo"[..]); 49 | assert_eq!(&buf[..], b"hello"); 50 | // Freeze the buffer so that it can be shared 51 | // 转换为Bytes,底层数据没有复制,仅改变了指针 52 | let a = buf.freeze(); 53 | // This does not allocate, instead `b` points to the same memory. 54 | let b = a.clone(); 55 | assert_eq!(&a[..], b"hello"); 56 | assert_eq!(&b[..], b"hello"); 57 | 58 | // Buf, BufMut trait 59 | println!("-----------------Buf, BufMut-----------------"); 60 | // 这两个特征提供了对缓冲区的读写访问, 61 | // 底层的数据在内存中可能是连续的,也可能是分散的, 62 | // 例如,Bytes是一个保证内存连续的缓冲区,但rope将字节存储在不相交的块中。 63 | // Buf和BufMut维护游标,跟踪底层字节存储中的当前位置。在读取或写入字节时,向前移动游标。 64 | // 乍一看,似乎Buf和BufMut在功能上与std::io::Read和std::io::Write重叠。然而,它们服务于不同的目的。 65 | // 读写操作可能会执行一个系统调用,这可能会失败。对Buf和BufMut的操作是绝对正确的。 66 | 67 | // 创建一个可变缓冲区 68 | let mut buf = BytesMut::new(); 69 | // 向缓冲区写入数据 70 | buf.put_u8(10); //需要实现BufMut trait 71 | buf.put_slice(b"hello world"); 72 | 73 | // 读取数据 74 | let mut cursor = buf.freeze(); 75 | 76 | // 从缓冲区中读取数据并移动游标 77 | let num = cursor.get_u8(); //需要实现Buf trait 78 | let message = String::from_utf8_lossy(cursor.chunk()); 79 | 80 | println!("Number: {}", num); 81 | println!("Message: {}", message); 82 | } 83 | -------------------------------------------------------------------------------- /c调用rust/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | -------------------------------------------------------------------------------- /c调用rust/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "c-to-rust" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | 7 | [lib] 8 | #name为编译生成之后的lib库的名字,生成lib2_3.a(或lib2_3.lib)静态库和其他一些编译之后东西 9 | name = "2_3" 10 | # 指定rustc编译成什么库类型,这里指定为静态库类型。 11 | crate-type = ["staticlib"] 12 | -------------------------------------------------------------------------------- /c调用rust/src/lib.rs: -------------------------------------------------------------------------------- 1 | // rustc默认编译产生rust自用的rlib格式库,要让rustc产生动态链接库或者静态链接库,需要显式指定。 2 | 3 | // 方法1: 在文件中指定。 4 | // 在文件头加上#![crate_type = “foo”], 5 | // 其中foo的可选类型有bin, lib, rlib, dylib, staticlib, 6 | // 默认(将由rustc自己决定), rlib格式,动态链接库,静态链接库。 7 | // 方法2: 编译时给rustc 传–crate-type参数,参数内容同上。 8 | // 方法3: 使用cargo,指定crate-type = [“foo”], foo可选类型同1。 9 | 10 | // #[no_mangle] 的作用是由于rust支持重载,所以函数名会被编译器进行混淆, 11 | // 就像c++一样,加上这个就可以不修改函数名。 12 | 13 | #![crate_type = "staticlib"] // 指定rustc编译成什么库类型,这里指定为静态库类型。 14 | 15 | #[unsafe(no_mangle)] 16 | pub extern "C" fn double_input(input: i32) -> i32 { 17 | input * 2 18 | } 19 | 20 | #[unsafe(no_mangle)] 21 | pub extern "C" fn third_input(input: i32) -> i32 { 22 | input * 3 23 | } 24 | 25 | // cargo build --release 26 | -------------------------------------------------------------------------------- /c调用rust/src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | extern int32_t double_input(int32_t input); 5 | extern int32_t third_input(int32_t input); 6 | 7 | // gcc -o test_c main.c 2_3.lib 8 | // ./test_c 9 | int main() { 10 | int input = 4; 11 | int output = double_input(input); 12 | int output2 = third_input(input); 13 | printf("%d * 2 = %d\n", input, output); 14 | printf("%d * 3 = %d\n", input, output2); 15 | return 0; 16 | } 17 | -------------------------------------------------------------------------------- /hello_macro/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /hello_macro/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "hello_macro" 7 | version = "0.1.0" 8 | dependencies = [ 9 | "hello_macro_derive", 10 | "syn", 11 | ] 12 | 13 | [[package]] 14 | name = "hello_macro_derive" 15 | version = "0.1.0" 16 | dependencies = [ 17 | "quote", 18 | "syn", 19 | ] 20 | 21 | [[package]] 22 | name = "proc-macro2" 23 | version = "1.0.67" 24 | source = "registry+https://github.com/rust-lang/crates.io-index" 25 | checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" 26 | dependencies = [ 27 | "unicode-ident", 28 | ] 29 | 30 | [[package]] 31 | name = "quote" 32 | version = "1.0.33" 33 | source = "registry+https://github.com/rust-lang/crates.io-index" 34 | checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" 35 | dependencies = [ 36 | "proc-macro2", 37 | ] 38 | 39 | [[package]] 40 | name = "syn" 41 | version = "2.0.37" 42 | source = "registry+https://github.com/rust-lang/crates.io-index" 43 | checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8" 44 | dependencies = [ 45 | "proc-macro2", 46 | "quote", 47 | "unicode-ident", 48 | ] 49 | 50 | [[package]] 51 | name = "unicode-ident" 52 | version = "1.0.8" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" 55 | -------------------------------------------------------------------------------- /hello_macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hello_macro" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | hello_macro_derive = { path = "./hello_macro_derive" } 10 | syn = "2.0.37" 11 | -------------------------------------------------------------------------------- /hello_macro/hello_macro_derive/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | -------------------------------------------------------------------------------- /hello_macro/hello_macro_derive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hello_macro_derive" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | # syn 和 quote 依赖包都是定义过程宏所必需的, 7 | # 同时,还需要在 [lib] 中将过程宏的开关开启 : proc-macro = true。 8 | [lib] 9 | proc-macro = true 10 | 11 | [dependencies] 12 | syn = "2.0" 13 | quote = "1.0" 14 | proc-macro2 = "1.0" 15 | -------------------------------------------------------------------------------- /hello_macro/hello_macro_derive/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate proc_macro; 2 | 3 | // proc_macro 包是 Rust 自带的,因此无需在 Cargo.toml 中引入依赖, 4 | // 它包含了相关的编译器 API,可以用于读取和操作 Rust 源代码。 5 | use proc_macro::TokenStream; 6 | use quote::quote; 7 | 8 | // syn 将字符串形式的 Rust 代码解析为一个 AST 树的数据结构, 9 | // 该数据结构可以在随后的 impl_hello_macro 函数中进行操作。 10 | // 最后,操作的结果又会被 quote 包转换回 Rust 代码。 11 | // 这些包非常关键,可以帮我们节省大量的精力, 12 | // 否则你需要自己去编写支持代码解析和还原的解析器,这可不是一件简单的任务! 13 | // use syn; 14 | use syn::DeriveInput; 15 | 16 | // 对于绝大多数过程宏而言,这段代码往往只在 impl_hello_macro(&ast) 中的实现有所区别, 17 | // 对于其它部分基本都是一致的,例如包的引入、宏函数的签名、语法树构建等。 18 | 19 | // 由于我们为 hello_macro_derive 函数标记了 #[proc_macro_derive(HelloMacro)], 20 | // 当用户使用 #[derive(HelloMacro)] 标记了他的类型后, hello_macro_derive 函数就将被调用。 21 | // 这里的秘诀就是特征名 HelloMacro,它就像一座桥梁,将用户的类型和过程宏联系在一起。 22 | #[proc_macro_derive(HelloMacro)] 23 | pub fn hello_macro_derive(input: TokenStream) -> TokenStream { 24 | // 基于 input 构建 AST 语法树 25 | let ast: DeriveInput = syn::parse(input).unwrap(); 26 | 27 | // 构建特征实现代码 28 | impl_hello_macro(&ast) 29 | } 30 | 31 | // 在 hello_macro_derive 函数中有 unwrap 的调用,也许会以为这是为了演示目的,没有做错误处理, 32 | // 实际上并不是的。由于该函数只能返回 TokenStream 而不是 Result, 33 | // 那么在报错时直接 panic 来抛出错误就成了相当好的选择。 34 | // 当然,这里实际上还是做了简化,在生产项目中,你应该通过 panic! 或 expect 抛出更具体的报错信息。 35 | 36 | // derive过程宏只能用在struct/enum/union上,多数用在结构体上,我们先来看一下一个结构体由哪些部分组成: 37 | // // vis,可视范围 ident,标识符 generic,范型 38 | // pub struct User <'a, T> { 39 | 40 | // // vis ident type 41 | // pub name: &'a T, //fields: 结构体的字段 42 | 43 | // } 44 | 45 | /* 46 | // syn::parse 调用会返回一个 DeriveInput 结构体来代表解析后的 Rust 代码: 47 | 48 | DeriveInput { 49 | // --snip-- 50 | vis: Visibility, 51 | generics: Generics 52 | ident: Ident { 53 | ident: "foo", 54 | span: #0 bytes(95..103) 55 | }, 56 | // Data是一个枚举,分别是DataStruct,DataEnum,DataUnion,这里以 DataStruct 为例 57 | data: Data( 58 | DataStruct { 59 | struct_token: Struct, 60 | fields: Fields, 61 | semi_token: Some( 62 | Semi 63 | ) 64 | } 65 | ) 66 | } 67 | 68 | 以上就是源代码 struct foo; 解析后的结果,里面有几点值得注意: 69 | - fields: Fields 是一个枚举类型,FieldsNamed,FieldsUnnamed,FieldsUnnamed, 70 | 分别表示显示命名结构(如例子所示),匿名字段的结构(例如 struct A(u8);), 71 | 和无字段定义的结构(例如 struct A;) 72 | - ident: "foo" 说明类型名称为 foo, ident 是标识符 identifier 的简写 73 | 74 | */ 75 | 76 | // 下面来看看如何构建特征实现的代码,也是过程宏的核心目标: 77 | fn impl_hello_macro(ast: &syn::DeriveInput) -> TokenStream { 78 | // 将结构体的名称赋予给 `name`,也就是 `name` 中会包含一个字段,它的值是字符串 "foo"。 79 | let name = &ast.ident; 80 | let gen1 = quote! { 81 | impl HelloMacro for #name { 82 | fn hello_macro() { 83 | println!("Hello, Macro! My name is {}!", stringify!(#name)); 84 | } 85 | } 86 | }; 87 | // 使用 quote! 可以定义我们想要返回的 Rust 代码。 88 | // 由于编译器需要的内容和 quote! 直接返回的不一样, 89 | // 因此还需要使用 .into 方法其转换为 TokenStream。 90 | // 大家注意到 #name 的使用了吗?这也是 quote! 提供的功能之一, 91 | // 如果想要深入了解 quote,可以看看[官方文档](https://docs.rs/quote)。 92 | gen1.into() 93 | 94 | // 上面 stringify! 是 Rust 提供的内置宏,可以将一个表达式(例如 1 + 2)在编译期 95 | // 转换成一个字符串字面值("1 + 2"),该字面量会直接打包进编译出的二进制文件中,具有 'static 生命周期。 96 | // 而 format! 宏会对表达式进行求值,最终结果是一个 String 类型。 97 | // 在这里使用 stringify! 有两个好处: 98 | // - #name 可能是一个表达式,我们需要它的字面值形式 99 | // - 可以减少一次 String 带来的内存分配 100 | } 101 | 102 | // 在运行之前,可以显示用 expand 展开宏,观察是否有错误或是否符合预期: 103 | // cargo expand 104 | -------------------------------------------------------------------------------- /hello_macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | // 在 `src/lib.rs` 中定义过程宏所需的 `HelloMacro` 特征和其关联函数: 2 | pub trait HelloMacro { 3 | fn hello_macro(); 4 | } 5 | // 然后在 `src/main.rs` 中编写主体代码 6 | -------------------------------------------------------------------------------- /hello_macro/src/main.rs: -------------------------------------------------------------------------------- 1 | use hello_macro::HelloMacro; 2 | use hello_macro_derive::HelloMacro; 3 | 4 | #[derive(HelloMacro)] 5 | struct Sunfei; 6 | 7 | #[derive(HelloMacro)] 8 | struct Sunface; 9 | 10 | fn main() { 11 | Sunfei::hello_macro(); 12 | Sunface::hello_macro(); 13 | // 简单明了的代码总是令人愉快,为了让代码运行起来,还需要定义下过程宏。 14 | // 目前只能在单独的包中定义过程宏,尽管未来这种限制会被取消,但是现在我们还得遵循这个规则。 15 | // 由于过程宏所在的包跟我们的项目紧密相连,因此将它放在项目之中。 16 | 17 | // 问题又来了,该如何在项目的 `src/main.rs` 中引用 `hello_macro_derive` 包的内容? 18 | // 方法有两种,第一种是将 `hello_macro_derive` 发布到 `crates.io` 或 `GitHub` 中, 19 | // 就像我们引用的其它依赖一样; 20 | // 另一种就是使用相对路径引入的本地化方式,修改 `hello_macro/Cargo.toml` 文件添加以下内容: 21 | 22 | // 学习过程更好的办法是通过展开宏来阅读和调试自己写的宏, 23 | // 这里需要用到一个 cargo-expand 的工具,可以通过下面的命令安装: 24 | // cargo install cargo-expand 25 | 26 | // 观察展开后的代码 27 | // rustup override set nightly 28 | // cargo expand --bin hello_macro 29 | } 30 | 31 | // #![feature(prelude_import)] 32 | // #[prelude_import] 33 | // use std::prelude::rust_2021::*; 34 | // #[macro_use] 35 | // extern crate std; 36 | // use hello_macro::HelloMacro; 37 | // use hello_macro_derive::HelloMacro; 38 | // struct Sunfei; 39 | // impl HelloMacro for Sunfei { 40 | // fn hello_macro() { 41 | // { 42 | // ::std::io::_print(format_args!("Hello, Macro! My name is {0}!\n", "Sunfei")); 43 | // }; 44 | // } 45 | // } 46 | // struct Sunface; 47 | // impl HelloMacro for Sunface { 48 | // fn hello_macro() { 49 | // { 50 | // ::std::io::_print( 51 | // format_args!("Hello, Macro! My name is {0}!\n", "Sunface"), 52 | // ); 53 | // }; 54 | // } 55 | // } 56 | // fn main() { 57 | // Sunfei::hello_macro(); 58 | // Sunface::hello_macro(); 59 | // } 60 | -------------------------------------------------------------------------------- /linked_list/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /linked_list/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "linked_list" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | tokio = { version = "1.15.0", features = ["full"] } 10 | -------------------------------------------------------------------------------- /minigrep/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /minigrep/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "minigrep" 7 | version = "0.1.0" 8 | -------------------------------------------------------------------------------- /minigrep/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "minigrep" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /minigrep/poem.txt: -------------------------------------------------------------------------------- 1 | I'm nobody! Who are you? 2 | 我啥也不是,你呢? 3 | Are you nobody, too? 4 | 牛逼如你也是无名之辈吗? 5 | Then there's a pair of us - don't tell! 6 | 那我们就是天生一对,嘘!别说话! 7 | They'd banish us, you know. 8 | 你知道,我们不属于这里。 9 | How dreary to be somebody! 10 | 因为这里属于没劲的大人物! 11 | How public, like a frog 12 | 他们就像青蛙一样呱噪, 13 | To tell your name the livelong day 14 | 成天将自己的大名 15 | To an admiring bog! 16 | 传遍整个无聊的沼泽! 17 | -------------------------------------------------------------------------------- /minigrep/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub fn run(cnf: &Config) -> Result<(), Box> { 2 | let contents = std::fs::read_to_string(&cnf.file_path)?; 3 | let result = if cnf.ignore_case { 4 | search_case_insensitive(&cnf.query, &contents) 5 | } else { 6 | search(&cnf.query, &contents) 7 | }; 8 | for line in result { 9 | println!("{}", line); 10 | } 11 | Ok(()) 12 | } 13 | 14 | pub struct Config { 15 | pub query: String, 16 | pub file_path: String, 17 | pub ignore_case: bool, 18 | } 19 | 20 | impl Config { 21 | // &[String] 是数组切片 22 | // pub fn build(args: &[String]) -> Result { 23 | // if args.len() < 3 { 24 | // return Err("not enough arguments"); 25 | // } 26 | // // grep How poem.txt -i 27 | // let query = args[1].clone(); 28 | // let file_path = args[2].clone(); 29 | // // 使用环境变量来判断是否忽略大小写。 30 | // // is_ok 方法是 Result 提供的,用于检查是否有值,有就返回 true,没有则返回 false 31 | // let ignore_case = std::env::var("IGNORE_CASE").is_ok(); 32 | // Ok(Config { 33 | // query, 34 | // file_path, 35 | // ignore_case, 36 | // }) 37 | // } 38 | 39 | // 数组索引会越界,为了安全性和简洁性,使用 Iterator 特征自带的 next 方法是一个更好的选择: 40 | pub fn build<'a>(args: impl Iterator) -> Result { 41 | let mut args = args.skip(1); 42 | let query = match args.next() { 43 | Some(arg) => arg, 44 | None => return Err("Didn't get a query string"), 45 | }; 46 | let file_path = match args.next() { 47 | Some(arg) => arg, 48 | None => return Err("Didn't get a file name"), 49 | }; 50 | let ignore_case = std::env::var("IGNORE_CASE").is_ok(); 51 | Ok(Config { 52 | query: query.to_string(), 53 | file_path: file_path.to_string(), 54 | ignore_case, 55 | }) 56 | } 57 | } 58 | 59 | pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> { 60 | //println!("query: {}", query); 61 | //println!("contents: {}", contents); 62 | 63 | // let mut results = Vec::new(); 64 | // for line in contents.lines() { 65 | // if line.contains(query) { 66 | // results.push(line); 67 | // } 68 | // } 69 | // results 70 | contents 71 | .lines() 72 | .filter(|line| line.contains(query)) 73 | .collect() 74 | } 75 | 76 | pub fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a str> { 77 | let query = query.to_lowercase(); 78 | let mut results = Vec::new(); 79 | 80 | for line in contents.lines() { 81 | if line.to_lowercase().contains(&query) { 82 | results.push(line); 83 | } 84 | } 85 | 86 | results 87 | } 88 | 89 | // in src/lib.rs 90 | #[cfg(test)] 91 | mod tests { 92 | use super::*; 93 | 94 | #[test] 95 | fn case_sensitive() { 96 | let query = "duct"; 97 | let contents = "\ 98 | Rust: 99 | safe, fast, productive. 100 | Pick three. 101 | Duct tape."; 102 | 103 | assert_eq!(vec!["safe, fast, productive."], search(query, contents)); 104 | } 105 | 106 | #[test] 107 | fn case_insensitive() { 108 | let query = "rUsT"; 109 | let contents = "\ 110 | Rust: 111 | safe, fast, productive. 112 | Pick three. 113 | Trust me."; 114 | 115 | assert_eq!( 116 | vec!["Rust:", "Trust me."], 117 | search_case_insensitive(query, contents) 118 | ); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /minigrep/src/main.rs: -------------------------------------------------------------------------------- 1 | // cargo run -- searchstring example-filename.txt 2 | 3 | // cargo run -- How poem.txt 4 | 5 | // $env:IGNORE_CASE=1;cargo run -- to poem.txt 6 | use minigrep::Config; 7 | fn main() { 8 | let args: Vec = std::env::args().collect(); 9 | let query_cnf = Config::build(args.iter()).unwrap_or_else(|err| { 10 | eprintln!("Problem parsing arguments: {err}"); 11 | std::process::exit(1); 12 | }); 13 | println!("Searching for {}", query_cnf.query); 14 | println!("In file {}", query_cnf.file_path); 15 | 16 | if let Err(e) = minigrep::run(&query_cnf) { 17 | eprintln!("Application error: {e}"); 18 | std::process::exit(1); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /minigrep/tests/search.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | 4 | #[test] 5 | fn one_result() { 6 | let query = "fast"; 7 | let contents = "\ 8 | Rust: 9 | safe, fast, productive. 10 | Pick three."; 11 | 12 | assert_eq!( 13 | vec!["safe, fast, productive."], 14 | minigrep::search(query, contents) 15 | ); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /rust_golang_ffi_demo/golang/cgo/rustdemo.h: -------------------------------------------------------------------------------- 1 | char* rustdemo(char* name); -------------------------------------------------------------------------------- /rust_golang_ffi_demo/golang/go.mod: -------------------------------------------------------------------------------- 1 | module go_rust 2 | 3 | go 1.20 4 | -------------------------------------------------------------------------------- /rust_golang_ffi_demo/golang/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // #cgo CFLAGS: -I./cgo 4 | // #cgo LDFLAGS: -L${SRCDIR} -lrustdemo 5 | // #include "rustdemo.h" 6 | import "C" 7 | import ( 8 | "fmt" 9 | ) 10 | 11 | func main() { 12 | name := C.CString("golang") 13 | result := C.GoString(C.rustdemo(name)) 14 | fmt.Printf("result: %s\n", result) 15 | } 16 | 17 | // unsafe 调用 dll 18 | /* 19 | package main 20 | 21 | import ( 22 | "C" 23 | "fmt" 24 | "syscall" 25 | "unsafe" 26 | ) 27 | 28 | // char* rustdemo(char* name); 29 | 30 | func main() { 31 | dllPath := "rustdemo.dll" 32 | rustdemo := syscall.NewLazyDLL(dllPath).NewProc("rustdemo") 33 | name := "golang" 34 | name_byte_ptr, err := syscall.BytePtrFromString(name) 35 | if err != nil { 36 | panic(err) 37 | } 38 | result_ptr, _, _ := rustdemo.Call(uintptr(unsafe.Pointer(name_byte_ptr))) 39 | // result := readCByte(result_ptr) 40 | // fmt.Printf("result: %s\n", result) 41 | result := C.GoString((*C.char)(unsafe.Pointer(result_ptr))) 42 | fmt.Printf("result: %s\n", result) 43 | } 44 | 45 | 46 | func readCByte(ptr uintptr) []byte { 47 | var result []byte 48 | for { 49 | b := (*byte)(unsafe.Pointer(ptr)) 50 | if *b == 0 { 51 | break 52 | } 53 | result = append(result, *b) 54 | ptr++ 55 | } 56 | return result 57 | } 58 | 59 | */ 60 | -------------------------------------------------------------------------------- /rust_golang_ffi_demo/golang/mainc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern char* rustdemo(char* name); 4 | 5 | // 将rustdemo.dll动态链接库放到当前目录下 6 | // gcc -o test_c main.c rustdemo.dll 7 | int main() { 8 | char* name = "Rust"; 9 | char* result = rustdemo(name); 10 | printf("%s\n", result); 11 | return 0; 12 | } 13 | -------------------------------------------------------------------------------- /rust_golang_ffi_demo/rustdemo/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | -------------------------------------------------------------------------------- /rust_golang_ffi_demo/rustdemo/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rustdemo" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | # [lib] 9 | # #name为编译生成之后的lib库的名字,生成libsay_hello.a(或libsay_hello.lib)静态库和其他一些编译之后东西 10 | # name = "say_hello" 11 | # # 指定rustc编译成什么库类型,这里指定为静态库类型。 12 | # crate-type = ["staticlib"] 13 | 14 | # [dependencies] 15 | # libc = "0.2" 16 | 17 | [lib] 18 | crate-type = ["cdylib"] 19 | [dependencies] 20 | libc = "0.2" 21 | -------------------------------------------------------------------------------- /rust_golang_ffi_demo/rustdemo/src/lib.rs: -------------------------------------------------------------------------------- 1 | // extern crate libc; 2 | // use std::ffi::{CStr, CString}; 3 | // #[no_mangle] 4 | // pub extern "C" fn rustdemo(name: *const libc::c_char) -> *const libc::c_char { 5 | // let cstr_name = unsafe { CStr::from_ptr(name) }; 6 | // let mut str_name = cstr_name.to_str().unwrap().to_string(); 7 | // println!("Rust get Input: \"{}\"", str_name); 8 | // let r_string: &str = " Rust say: Hello Go "; 9 | // str_name.push_str(r_string); 10 | // CString::new(str_name).unwrap().into_raw() 11 | // } 12 | 13 | // #![crate_type = "staticlib"] // 指定rustc编译成什么库类型,这里指定为静态库类型。 14 | // use std::ffi::{CStr, CString}; 15 | // #[no_mangle] 16 | // pub extern "C" fn rust_say_hello(name: *const libc::c_char) -> *const libc::c_char { 17 | // let cstr_name = unsafe { CStr::from_ptr(name) }; 18 | // let mut str_name = cstr_name.to_str().unwrap().to_string(); 19 | // println!("Rust get Input: \"{}\"", str_name); 20 | // let r_string: &str = " Rust say: Hello Go "; 21 | // str_name.push_str(r_string); 22 | // CString::new(str_name).unwrap().into_raw() 23 | // } 24 | extern crate libc; 25 | use std::ffi::{CStr, CString}; 26 | #[unsafe(no_mangle)] 27 | pub extern "C" fn rustdemo(name: *const libc::c_char) -> *const libc::c_char { 28 | let cstr_name = unsafe { CStr::from_ptr(name) }; 29 | let mut str_name = cstr_name.to_str().unwrap().to_string(); 30 | println!("Rust get Input: \"{}\"", str_name); 31 | let r_string: &str = " Rust say: Hello Go "; 32 | str_name.push_str(r_string); 33 | CString::new(str_name).unwrap().into_raw() 34 | } 35 | -------------------------------------------------------------------------------- /rust调用c/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /rust调用c/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "cc" 7 | version = "1.0.79" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" 10 | 11 | [[package]] 12 | name = "libc" 13 | version = "0.2.142" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317" 16 | 17 | [[package]] 18 | name = "rust-to-c" 19 | version = "0.1.0" 20 | dependencies = [ 21 | "cc", 22 | "libc", 23 | ] 24 | -------------------------------------------------------------------------------- /rust调用c/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust-to-c" 3 | version = "0.1.0" 4 | edition = "2024" 5 | # Cargo 会先编译和执行该构建脚本,然后再去构建整个项目。 6 | build = "build.rs" 7 | 8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 9 | 10 | [dependencies] 11 | libc = "0.2" 12 | 13 | [build-dependencies] 14 | cc = "1.0" 15 | -------------------------------------------------------------------------------- /rust调用c/build.rs: -------------------------------------------------------------------------------- 1 | extern crate cc; 2 | 3 | // build.rs 4 | 5 | /* 6 | 在项目中使用基于 C 或 C++ 的本地库,而这种使用场景恰恰是构建脚本非常擅长的。 7 | 8 | use std::process::Command; 9 | use std::env; 10 | use std::path::Path; 11 | 12 | fn main() { 13 | let out_dir = env::var("OUT_DIR").unwrap(); 14 | 15 | Command::new("gcc").args(&["src/hello.c", "-c", "-fPIC", "-o"]) 16 | .arg(&format!("{}/hello.o", out_dir)) 17 | .status().unwrap(); 18 | Command::new("ar").args(&["crus", "libhello.a", "hello.o"]) 19 | .current_dir(&Path::new(&out_dir)) 20 | .status().unwrap(); 21 | 22 | println!("cargo:rustc-link-search=native={}", out_dir); 23 | println!("cargo:rustc-link-lib=static=hello"); 24 | println!("cargo:rerun-if-changed=src/hello.c"); 25 | } 26 | 27 | */ 28 | 29 | // 首先,构建脚本将我们的 C 文件通过 gcc 编译成目标文件, 30 | // 然后使用 ar 将该文件转换成一个静态库,最后告诉 Cargo 我们的输出内容在 out_dir 中, 31 | // 编译器要在这里搜索相应的静态库,最终通过 -l static-hello 标志将我们的项目跟 libhello.a 进行静态链接。 32 | 33 | // 但是这种硬编码的解决方式有几个问题: 34 | // gcc 命令的跨平台性是受限的,例如 Windows 下就难以使用它, 35 | // 甚至于有些 Unix 系统也没有 gcc 命令,同样,ar 也有这个问题 36 | // 这些命令往往不会考虑交叉编译的问题,如果我们要为 Android 平台进行交叉编译, 37 | // 那么 gcc 很可能无法输出一个 ARM 的可执行文件 38 | // 但是别怕,构建依赖 [build-dependencies] 解君忧:社区中已经有现成的解决方案, 39 | // 可以让这种任务得到更容易的解决。例如文章开头提到的 cc 包。 40 | 41 | // 简单来说,cc 包将构建脚本使用 C 的需求进行了抽象: 42 | // cc 会针对不同的平台调用合适的编译器:windows 下调用 MSVC, MinGW 下调用 gcc, Unix 平台调用 cc 等 43 | // 在编译时会考虑到平台因素,例如将合适的标志传给正在使用的编译器 44 | // 其它环境变量,例如 OPT_LEVEL、DEBUG 等会自动帮我们处理 45 | // 标准输出和 OUT_DIR 的位置也会被 cc 所处理 46 | 47 | fn main() { 48 | // 编译成静态库 49 | cc::Build::new().file("src/double.c").compile("libdouble.a"); 50 | cc::Build::new().file("src/third.c").compile("libthird.a"); 51 | } 52 | -------------------------------------------------------------------------------- /rust调用c/src/double.c: -------------------------------------------------------------------------------- 1 | int double_input(int input) { 2 | return input * 2; 3 | } 4 | -------------------------------------------------------------------------------- /rust调用c/src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate libc; 2 | 3 | unsafe extern "C" { 4 | fn double_input(input: libc::c_int) -> libc::c_int; 5 | fn third_input(input: libc::c_int) -> libc::c_int; 6 | } 7 | 8 | fn main() { 9 | let input = 4; 10 | let output = unsafe { double_input(input) }; 11 | let output2: i32 = unsafe { third_input(input) }; 12 | println!("{} * 3 = {}", input, output2); 13 | println!("{} * 2 = {}", input, output); 14 | } 15 | -------------------------------------------------------------------------------- /rust调用c/src/third.c: -------------------------------------------------------------------------------- 1 | int third_input(int input) { 2 | return input * 3; 3 | } 4 | -------------------------------------------------------------------------------- /sayhello_lib/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | -------------------------------------------------------------------------------- /sayhello_lib/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sayhello_lib" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | 10 | # [lib] 11 | # name = "my_library" 12 | # crate-type = ["cdylib"] 13 | -------------------------------------------------------------------------------- /sayhello_lib/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub fn add(left: usize, right: usize) -> usize { 2 | left + right 3 | } 4 | 5 | pub fn say_hello(name: &str) { 6 | println!("Hello, {}!", name); 7 | } 8 | 9 | #[cfg(test)] 10 | mod tests { 11 | use super::*; 12 | 13 | #[test] 14 | fn it_works() { 15 | let result = add(2, 2); 16 | assert_eq!(result, 4); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("Hello, world!"); 3 | } 4 | -------------------------------------------------------------------------------- /study_test/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | -------------------------------------------------------------------------------- /study_test/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "study_test" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | 10 | # standard crate data is left out 11 | [dev-dependencies] 12 | pretty_assertions = "1" 13 | criterion = "0.3" 14 | 15 | 16 | # cargo bench 命令会自动运行 benchmarks 目录下的所有基准测试 17 | [[bench]] 18 | name = "my_benchmark" 19 | harness = false 20 | -------------------------------------------------------------------------------- /study_test/benches/my_benchmark.rs: -------------------------------------------------------------------------------- 1 | use criterion::{Criterion, black_box, criterion_group, criterion_main}; 2 | 3 | fn fibonacci(n: u64) -> u64 { 4 | match n { 5 | 0 => 1, 6 | 1 => 1, 7 | n => fibonacci(n - 1) + fibonacci(n - 2), 8 | } 9 | } 10 | 11 | // https://bheisler.github.io/criterion.rs/book/getting_started.html 12 | fn criterion_benchmark(c: &mut Criterion) { 13 | c.bench_function("fib 20", |b| b.iter(|| fibonacci(black_box(20)))); 14 | } 15 | 16 | criterion_group!(benches, criterion_benchmark); 17 | criterion_main!(benches); 18 | -------------------------------------------------------------------------------- /study_test/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub fn add(left: usize, right: usize) -> usize { 2 | left + right 3 | } 4 | 5 | pub fn add_two(left: i32) -> i32 { 6 | left + 2 7 | } 8 | 9 | // `tests` 就是一个测试模块,`it_works` 则是我们的主角:测试函数。 10 | // 测试函数需要使用 `test` 属性进行标注。 11 | // 关于属性( `attribute` ),我们在之前的章节已经见过类似的 `derive`, 12 | // 使用它可以派生自动实现的 `Debug` 、`Copy` 等特征, 13 | // 同样的,使用 `test` 属性,我们也可以获取 Rust 提供的测试特性。 14 | // 当然,在测试模块 `tests` 中,还可以定义非测试函数, 这些函数可以用于设置环境或执行一些通用操作: 15 | // 例如为部分测试函数提供某个通用的功能,这种功能就可以抽象为一个非测试函数。 16 | 17 | #[cfg(test)] 18 | mod tests { 19 | use super::*; // 使用 super::* 导入父模块中的所有内容,这样就可以直接使用 add 函数了 20 | 21 | // `#[test]` 属性标注了 `it_works` 函数,这样 Rust 就知道这是一个测试函数。 22 | #[test] 23 | fn it_works() { 24 | let result = add(2, 2); 25 | assert_eq!(result, 4); 26 | } 27 | } 28 | 29 | pub fn greeting(name: &str) -> String { 30 | format!("Hello {}!", name) 31 | } 32 | #[cfg(test)] 33 | mod tests2 { 34 | use super::*; 35 | 36 | #[test] 37 | fn greeting_contains_name() { 38 | // 默认情况下,如果测试通过,那写入标准输出的内容是不会显示在测试结果中的。 39 | // 除非:$ cargo test -- --show-output 40 | println!("测试 greeting_contains_name"); 41 | let result = greeting("Sunface"); 42 | let target = "孙飞"; 43 | // 使用 `assert!` 自定义失败信息 44 | assert!( 45 | result.contains(target), 46 | "你的问候中并没有包含目标姓名 {} ,你的问候是 `{}`", 47 | target, 48 | result 49 | ); 50 | } 51 | } 52 | 53 | pub struct Guess { 54 | _value: i32, 55 | } 56 | 57 | impl Guess { 58 | pub fn new(value: i32) -> Guess { 59 | if value < 1 || value > 100 { 60 | panic!("Guess value must be between 1 and 100, got {}.", value); 61 | } 62 | 63 | Guess { _value: value } 64 | } 65 | } 66 | 67 | #[cfg(test)] 68 | mod tests3 { 69 | use super::*; 70 | 71 | // 测试 panic 72 | #[test] 73 | #[should_panic] 74 | fn greater_than_100() { 75 | Guess::new(200); 76 | } 77 | 78 | // 虽然 `panic` 被成功测试到,但是如果代码发生的 `panic` 和我们预期的 `panic` 不符合呢? 79 | // 鉴于此,我们可以使用可选的参数 `expected` 来说明预期的 `panic` 长啥样。 80 | // `expected` 的字符串和实际 `panic` 的字符串可以不同,前者只需要是后者的字符串前缀即可。 81 | #[test] 82 | #[should_panic(expected = "Guess value must be between 1 and 100")] 83 | fn greater_than_100_2() { 84 | Guess::new(200); 85 | } 86 | } 87 | 88 | #[cfg(test)] 89 | mod tests4 { 90 | use super::*; // 使用 super::* 导入父模块中的所有内容 91 | 92 | // 在测试中使用 `?` 操作符进行链式调用 93 | #[test] 94 | fn it_works() -> Result<(), String> { 95 | if add(2, 2) == 4 { 96 | Ok(()) 97 | } else { 98 | // 如果返回 `Err`,测试就会失败 99 | Err(String::from("two plus two does not equal four")) 100 | } 101 | } 102 | } 103 | 104 | #[cfg(test)] 105 | mod tests5 { 106 | use super::*; 107 | // 引入只在开发测试场景使用的外部依赖 108 | use pretty_assertions::assert_eq; // 该包仅能用于测试,提供彩色字体的结果对比。 109 | 110 | #[test] 111 | fn test_add() { 112 | assert_eq!(add(2, 3), 7); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /study_test/tests/add_test.rs: -------------------------------------------------------------------------------- 1 | use study_test; 2 | 3 | #[test] 4 | fn it_adds_two() { 5 | assert_eq!(4, study_test::add_two(2)); 6 | } 7 | -------------------------------------------------------------------------------- /timer_future/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | -------------------------------------------------------------------------------- /timer_future/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "timer_future" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | futures = "0.3.5" 10 | -------------------------------------------------------------------------------- /timer_future/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | future::Future, 3 | pin::Pin, 4 | sync::{Arc, Mutex}, 5 | task::{Context, Poll, Waker}, 6 | thread, 7 | time::Duration, 8 | }; 9 | 10 | // 实现 `Future` 定时器,之前提到: 新建线程在睡眠结束后会需要将状态同步给定时器 `Future` , 11 | // 由于是多线程环境,我们需要使用 `Arc>` 来作为一个共享状态,用于在新线程和 `Future` 定时器间共享。 12 | pub struct TimerFuture { 13 | shared_state: Arc>, 14 | } 15 | 16 | /// 在Future和等待的线程间共享状态 17 | struct SharedState { 18 | /// 定时(睡眠)是否结束 19 | completed: bool, 20 | 21 | /// 当睡眠结束后,线程可以用`waker`通知`TimerFuture`来唤醒任务 22 | waker: Option, 23 | } 24 | impl Future for TimerFuture { 25 | type Output = (); 26 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 27 | println!("TimerFuture poll"); 28 | // 通过检查共享状态,来确定定时器是否已经完成 29 | let mut shared_state = self.shared_state.lock().unwrap(); 30 | if shared_state.completed { 31 | println!("TimerFuture poll completed, return Poll::Ready"); 32 | Poll::Ready(()) 33 | } else { 34 | // 设置`waker`,这样新线程在睡眠(计时)结束后可以唤醒当前的任务,接着再次对`Future`进行`poll`操作, 35 | // 36 | // 下面的`clone`每次被`poll`时都会发生一次,实际上,应该是只`clone`一次更加合理。 37 | // 选择每次都`clone`的原因是: `TimerFuture`可以在执行器的不同任务间移动,如果只克隆一次, 38 | // 那么获取到的`waker`可能已经被篡改并指向了其它任务,最终导致执行器运行了错误的任务 39 | shared_state.waker = Some(cx.waker().clone()); 40 | println!("TimerFuture poll not completed, return Poll::Pending"); 41 | Poll::Pending 42 | } 43 | } 44 | } 45 | 46 | // 代码很简单,只要新线程设置了 `shared_state.completed = true` ,那任务就能顺利结束。 47 | // 如果没有设置,会为当前的任务克隆一份 `Waker` ,这样新线程就可以使用它来唤醒当前的任务。 48 | 49 | // 最后,再来创建一个 API 用于构建定时器和启动计时线程: 50 | impl TimerFuture { 51 | /// 创建一个新的`TimerFuture`,在指定的时间结束后,该`Future`可以完成 52 | pub fn new(duration: Duration) -> Self { 53 | println!("TimerFuture new"); 54 | let shared_state = Arc::new(Mutex::new(SharedState { 55 | completed: false, 56 | waker: None, 57 | })); 58 | 59 | // 创建新线程 60 | let thread_shared_state = shared_state.clone(); 61 | thread::spawn(move || { 62 | // 睡眠指定时间实现计时功能 63 | thread::sleep(duration); 64 | let mut shared_state = thread_shared_state.lock().unwrap(); 65 | // 通知执行器定时器已经完成,可以继续`poll`对应的`Future`了 66 | shared_state.completed = true; 67 | if let Some(waker) = shared_state.waker.take() { 68 | waker.wake() 69 | } 70 | }); 71 | 72 | TimerFuture { shared_state } 73 | } 74 | } 75 | 76 | // 至此,一个简单的定时器 Future 就已创建成功, 77 | // 那么该如何使用它呢?我们需要创建一个执行器,才能让程序动起来。 78 | 79 | // Rust 的 Future 是惰性的:只有屁股上拍一拍,它才会努力动一动。 80 | // 其中一个推动它的方式就是在 async 函数中使用 .await 来调用另一个 async 函数, 81 | // 但是这个只能解决 async 内部的问题,那么这些最外层的 async 函数,谁来推动它们运行呢? 82 | // 答案就是我们之前多次提到的执行器 executor (例如上一章节中的block_on函数) 。 83 | 84 | // 执行器会管理一批 Future (最外层的 async 函数),然后通过不停地 poll 推动它们直到完成。 85 | // 最开始,执行器会先 poll 一次 Future ,后面就不会主动去 poll 了, 86 | // 而是等待 Future 通过调用 wake 函数来通知它可以继续, 87 | // 它才会继续去 poll 。这种 wake 通知然后 poll 的方式会不断重复,直到 Future 完成。 88 | -------------------------------------------------------------------------------- /unsafe_rust/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /unsafe_rust/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "unsafe_rust" 7 | version = "0.1.0" 8 | -------------------------------------------------------------------------------- /unsafe_rust/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "unsafe_rust" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /unsafe_rust/src/libadd.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/doraemonkeys/rust_code_snippets/9a0b29d113cf59733fc1580997c9008cb822ce87/unsafe_rust/src/libadd.so -------------------------------------------------------------------------------- /web服务器/单线程版本的_web_服务器/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /web服务器/单线程版本的_web_服务器/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Hello! 7 | 8 | 9 | 10 |

Oops!

11 |

Sorry, I don't know what you're asking for.

12 | 13 | 14 | -------------------------------------------------------------------------------- /web服务器/单线程版本的_web_服务器/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "单线程版本的_web_服务器" 7 | version = "0.1.0" 8 | -------------------------------------------------------------------------------- /web服务器/单线程版本的_web_服务器/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "单线程版本的_web_服务器" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /web服务器/单线程版本的_web_服务器/hello.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Hello! 7 | 8 | 9 | 10 |

Hello!

11 |

Hi from Rust

12 | 13 | 14 | -------------------------------------------------------------------------------- /web服务器/单线程版本的_web_服务器/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::fs; 2 | use std::io::prelude::*; 3 | use std::net::TcpListener; 4 | use std::net::TcpStream; 5 | 6 | // 单线程版本可以修改为多线程甚至于线程池来实现并发处理, 7 | // 但是线程还是太重了,使用 async 实现 Web 服务器才是最适合的。 8 | fn main() { 9 | // 监听本地端口 7878 ,等待 TCP 连接的建立 10 | let listener = TcpListener::bind("127.0.0.1:7878").unwrap(); 11 | 12 | // 阻塞等待请求的进入 13 | // incoming 会返回一个迭代器,它每一次迭代都会返回一个新的连接 stream(客户端发起,web服务器监听接收), 14 | // 因此,接下来要做的就是从 stream 中读取数据,然后返回处理后的结果。 15 | for stream in listener.incoming() { 16 | let stream = stream.unwrap(); 17 | 18 | let res = handle_connection(stream); 19 | if res.is_err() { 20 | println!("Error: {:?}", res); 21 | } 22 | } 23 | } 24 | 25 | fn handle_connection(mut stream: TcpStream) -> Result<(), Box> { 26 | // 从连接中顺序读取 1024 字节数据 27 | let mut buffer = [0; 1024]; 28 | let n = stream.read(&mut buffer)?; 29 | 30 | println!("Request:\n {}", String::from_utf8_lossy(&buffer[..n])); 31 | 32 | let get = b"GET / HTTP/1.1\r\n"; 33 | 34 | // 处理HTTP协议头,若不符合则返回404和对应的 `html` 文件 35 | let (status_line, filename) = if buffer.starts_with(get) { 36 | ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") 37 | } else { 38 | ("HTTP/1.1 404 NOT FOUND\r\n\r\n", "404.html") 39 | }; 40 | let contents = fs::read_to_string(filename)?; 41 | 42 | // 将回复内容写入连接缓存中 43 | let response = format!("{status_line}{contents}"); 44 | stream.write_all(response.as_bytes())?; 45 | // 使用 flush 将缓存中的内容发送到客户端 46 | stream.flush()?; 47 | Ok(()) 48 | } 49 | -------------------------------------------------------------------------------- /web服务器/实践多线程_web服务器/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /web服务器/实践多线程_web服务器/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 你好! 7 | 8 | 9 | 10 |

很抱歉!

11 |

由于运维删库跑路,我们的数据全部丢失,总监也已经准备跑路,88

12 | 13 | 14 | -------------------------------------------------------------------------------- /web服务器/实践多线程_web服务器/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "practice_thread_web_server" 7 | version = "0.1.0" 8 | -------------------------------------------------------------------------------- /web服务器/实践多线程_web服务器/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | #name = "实践多线程_web服务器" 3 | name = "practice_thread_web_server" 4 | version = "0.1.0" 5 | edition = "2024" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | -------------------------------------------------------------------------------- /web服务器/实践多线程_web服务器/hello.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Hello! 7 | 8 | 9 | 10 |

Hello!

11 |

Hi from Rust

12 | 13 | 14 | -------------------------------------------------------------------------------- /web服务器/实践多线程_web服务器/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{Arc, Mutex, mpsc}; 2 | 3 | pub struct ThreadPool { 4 | workers: Vec>, 5 | sender: Option>, 6 | } 7 | 8 | type Job = Box; 9 | struct Worker { 10 | id: usize, 11 | thread: std::thread::JoinHandle<()>, 12 | } 13 | 14 | impl Worker { 15 | fn new(id: usize, receiver: Arc>>) -> Worker { 16 | let thread = std::thread::spawn(move || { 17 | loop { 18 | let message = receiver.lock().unwrap().recv(); 19 | if message.is_err() { 20 | println!("Worker {id} disconnected; shutting down."); 21 | break; 22 | } 23 | let job = message.unwrap(); 24 | println!("Worker {id} got a job; executing."); 25 | 26 | job(); 27 | } 28 | }); 29 | 30 | Worker { id, thread } 31 | } 32 | } 33 | 34 | // - ThreadPool 拥有不错的文档注释,甚至包含了可能 panic 的情况, 35 | // 通过 cargo doc --open 可以访问文档注释 36 | impl ThreadPool { 37 | /// Create a new ThreadPool. 38 | /// 39 | /// The size is the number of threads in the pool. 40 | /// 41 | /// # Panics 42 | /// 43 | /// The `new` function will panic if the size is zero. 44 | pub fn new(size: usize) -> ThreadPool { 45 | assert!(size > 0); 46 | 47 | let (sender, receiver) = mpsc::channel(); 48 | 49 | let receiver = Arc::new(Mutex::new(receiver)); 50 | 51 | // 学过多线程一章后,大家应该知道 `thread::spawn` 虽然是生成线程最好的方式, 52 | // 但是它会立即执行传入的任务,然而,在我们的使用场景中,创建线程和执行任务明显是要分离的, 53 | // 因此标准库看起来不再适合。 54 | // 可以考虑创建一个 `Worker` 结构体,作为 `ThreadPool` 和任务线程联系的桥梁, 55 | // 它的任务是获得将要执行的代码,然后在具体的线程中去执行。 56 | // 想象一个场景:一个餐馆,`Worker` 等待顾客的点餐,然后将具体的点餐信息传递给厨房,感觉类似服务员? 57 | let mut workers = Vec::with_capacity(size); 58 | for id in 0..size { 59 | // create some threads and store them in the vector 60 | workers.push(Some(Worker::new(id, Arc::clone(&receiver)))); 61 | } 62 | 63 | ThreadPool { 64 | workers, 65 | sender: Some(sender), 66 | } 67 | } 68 | } 69 | 70 | impl ThreadPool { 71 | pub fn execute(&self, f: F) 72 | where 73 | F: FnOnce() + Send + 'static, 74 | { 75 | let job = Box::new(f); 76 | 77 | if self.sender.is_none() { 78 | return; 79 | } 80 | let res = self.sender.as_ref().unwrap().send(job); 81 | if res.is_err() { 82 | println!("Error: {:?}", res); 83 | } 84 | } 85 | } 86 | 87 | // 当线程池被 drop 时,需要等待所有的子线程完成它们的工作,然后再退出 88 | impl Drop for ThreadPool { 89 | fn drop(&mut self) { 90 | drop(self.sender.take()); 91 | for worker in &mut self.workers { 92 | // 对于 Option 类型,可以使用 take 方法拿走内部值的所有权, 93 | // 同时留下一个 None 在风中孤独凌乱。 94 | if let Some(worker) = worker.take() { 95 | // 虽然调用了 join ,但是目标线程依然不会停止,原因在于它们在无限的 loop 循环等待, 96 | // 需要借用 channel 的 drop 机制:释放 sender发送端后,receiver 接收端会收到报错, 97 | // 然后再退出即可。 98 | println!("Shutting down worker {}", worker.id); 99 | worker.thread.join().unwrap(); 100 | } 101 | } 102 | println!("All workers are shutdown."); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /web服务器/实践多线程_web服务器/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | io::{BufReader, prelude::*}, 3 | net::{TcpListener, TcpStream}, 4 | }; 5 | 6 | use practice_thread_web_server::ThreadPool; 7 | 8 | // 线程池包含一组已生成的线程,它们时刻等待着接收并处理新的任务。 9 | // 当程序接收到新任务时,它会将线程池中的一个线程指派给该任务,在该线程忙着处理时, 10 | // 新来的任务会交给池中剩余的线程进行处理。 11 | // 最终,当执行任务的线程处理完后,它会被重新放入到线程池中,准备处理新任务。 12 | 13 | // 假设线程池中包含 N 个线程,那么可以推断出,服务器将拥有并发处理 N 个请求连接的能力,从而增加服务器的吞吐量。 14 | // 同时,我们将限制线程池中的线程数量,以保护服务器免受拒绝服务攻击(DoS)的影响: 15 | // 如果针对每个请求创建一个新线程,那么一个人向我们的服务器发出1000万个请求,会直接耗尽资源, 16 | // 导致后续用户的请求无法被处理,这也是拒绝服务名称的来源。 17 | 18 | // 因此,还需对线程池进行一定的架构设计,首先是设定最大线程数的上限,其次维护一个请求队列。 19 | // 池中的线程去队列中依次弹出请求并处理。这样就可以同时并发处理 N 个请求,其中 N 是线程数。 20 | // 但聪明的读者可能会想到,假如每个请求依然耗时很长,那请求队列依然会堆积, 21 | // 后续的用户请求还是需要等待较长的时间,毕竟你也就 N 个线程,但总归比单线程要强 N 倍吧 :D 22 | // 当然,线程池依然是较为传统的提升吞吐方法, 23 | // 比较新的有:单线程异步 IO,例如 redis;多线程异步 IO,例如 Rust 的主流 web 框架。 24 | 25 | fn main() { 26 | let listener = TcpListener::bind("127.0.0.1:7878").unwrap(); 27 | let pool = ThreadPool::new(4); 28 | 29 | for stream in listener.incoming().take(2) { 30 | let stream = stream.unwrap(); 31 | 32 | pool.execute(|| { 33 | let err = handle_connection(stream); 34 | if let Err(e) = err { 35 | println!("handle_connection error: {}", e); 36 | } 37 | }); 38 | } 39 | println!("Shutting down."); 40 | // 即便主线程退出,只要子线程还在运行,程序就不会终止。 41 | } 42 | 43 | fn handle_connection(mut stream: TcpStream) -> Result<(), Box> { 44 | let buf_reader = BufReader::new(&mut stream); 45 | let request_line = buf_reader.lines().next().ok_or("bad request")??; 46 | 47 | let (status_line, filename) = match &request_line[..] { 48 | "GET / HTTP/1.1" => ("HTTP/1.1 200 OK", "hello.html"), 49 | "GET /sleep HTTP/1.1" => { 50 | std::thread::sleep(std::time::Duration::from_secs(5)); 51 | ("HTTP/1.1 200 OK", "hello.html") 52 | } 53 | _ => ("HTTP/1.1 404 NOT FOUND", "404.html"), 54 | }; 55 | 56 | let contents = std::fs::read_to_string(filename)?; 57 | let length = contents.len(); 58 | 59 | let response = format!("{status_line}\r\nContent-Length: {length}\r\n\r\n{contents}"); 60 | 61 | stream.write_all(response.as_bytes())?; 62 | Ok(()) 63 | } 64 | -------------------------------------------------------------------------------- /web服务器/异步_web服务器/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /web服务器/异步_web服务器/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 你好! 7 | 8 | 9 | 10 |

很抱歉!

11 |

由于运维删库跑路,我们的数据全部丢失,总监也已经准备跑路,88

12 | 13 | 14 | -------------------------------------------------------------------------------- /web服务器/异步_web服务器/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "异步_web服务器" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | futures = "0.3.28" 10 | [dependencies.async-std] 11 | version = "1.12" 12 | features = ["attributes"] 13 | -------------------------------------------------------------------------------- /web服务器/异步_web服务器/hello.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Hello! 7 | 8 | 9 | 10 |

Hello!

11 |

Hi from Rust

12 | 13 | 14 | -------------------------------------------------------------------------------- /web服务器/异步_web服务器/src/main.rs: -------------------------------------------------------------------------------- 1 | use async_std::net::TcpListener; 2 | use async_std::net::TcpStream; 3 | use async_std::prelude::*; 4 | use futures::stream::StreamExt; 5 | 6 | #[async_std::main] 7 | async fn main() { 8 | // 下面的例子将演示如何使用一个异步运行时 async-std 来让之前的 async fn 函数运行起来, 9 | // 该运行时允许使用属性 #[async_std::main] 将我们的 fn main 函数变成 async fn main , 10 | // 这样就可以在 main 函数中直接调用其它 async 函数,否则你得用之前章节的 block_on 11 | // 方法来让 main 去阻塞等待异步函数的完成,但是这种简单粗暴的阻塞等待方式并不灵活。 12 | let listener = TcpListener::bind("127.0.0.1:7878").await.unwrap(); 13 | // listener.incoming() 是阻塞的迭代器。当 listener 在等待连接时,执行器是无法执行其它 Future 的, 14 | // 而且只有在我们处理完已有的连接后,才能接收新的连接。 15 | // 解决方法是将 listener.incoming() 从一个阻塞的迭代器变成一个非阻塞的 Stream。 16 | // for stream in listener.incoming() { 17 | // let stream = stream.unwrap(); 18 | // // 警告,这里无法并发 19 | // handle_connection(stream).await; 20 | // } 21 | 22 | // 在将数据读写改造成异步后,现在该函数也彻底变成了异步的版本,因此一次慢请求不再会阻止其它请求的运行。 23 | listener 24 | .incoming() 25 | .for_each_concurrent(/* limit */ None, |tcpstream| async move { 26 | let tcpstream = tcpstream.unwrap(); 27 | handle_connection(tcpstream).await; 28 | }) 29 | .await; 30 | } 31 | 32 | // 该修改会将函数的返回值从 () 变成 Future ,因此直接运行将不再有任何效果, 33 | // 只用通过 .await 或执行器的 poll 调用后才能获取 Future 的结果。 34 | async fn handle_connection(mut stream: TcpStream) { 35 | let mut buffer = [0; 1024]; 36 | stream.read(&mut buffer).await.unwrap(); 37 | 38 | let get = b"GET / HTTP/1.1\r\n"; 39 | // 现在运行服务器,并访问 127.0.0.1:7878/sleep, 你会发现只有在完成第一个用户请求(5 秒后), 40 | // 才能开始处理第二个用户请求 127.0.0.1:7878。现在再来看看该如何解决这个问题,让请求并发起来。 41 | let sleep = b"GET /sleep HTTP/1.1\r\n"; 42 | 43 | let (status_line, filename) = if buffer.starts_with(get) { 44 | ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") 45 | } else if buffer.starts_with(sleep) { 46 | // 在内部睡眠 5 秒,模拟一次用户慢请求,需要注意的是, 47 | // 我们并没有使用 std::thread::sleep 进行睡眠,原因是该函数是阻塞的, 48 | // 它会让当前线程陷入睡眠中,导致其它任务无法继续运行! 49 | // 因此我们需要一个睡眠函数 async_std::task::sleep, 50 | // 它仅会让当前的任务陷入睡眠,然后该任务会让出线程的控制权,这样线程就可以继续运行其它任务。 51 | // 光把函数变成 async 往往是不够的,还需要将它内部的代码也都变成异步兼容的,阻塞线程绝对是不可行的。 52 | async_std::task::sleep(std::time::Duration::from_secs(5)).await; 53 | ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") 54 | } else { 55 | ("HTTP/1.1 404 NOT FOUND\r\n\r\n", "404.html") 56 | }; 57 | let contents = std::fs::read_to_string(filename).unwrap(); 58 | 59 | let response = format!("{status_line}{contents}"); 60 | stream.write(response.as_bytes()).await.unwrap(); 61 | stream.flush().await.unwrap(); 62 | } 63 | 64 | // 在之前的代码中,我们使用了自己实现的简单的执行器来进行 .await 或 poll , 65 | // 实际上这只是为了学习原理,在实际项目中,需要选择一个三方的 async 运行时来实现相关的功能。 66 | // 现在先选择 async-std ,该包的最大优点就是跟标准库的 API 类似,相对来说更简单易用。 67 | -------------------------------------------------------------------------------- /web服务器/异步多线程_web服务器/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /web服务器/异步多线程_web服务器/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Hello! 7 | 8 | 9 | 10 |

Oops!

11 |

Sorry, I don't know what you're asking for.

12 | 13 | 14 | -------------------------------------------------------------------------------- /web服务器/异步多线程_web服务器/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "异步多线程_web服务器" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | futures = "0.3.28" 10 | [dependencies.async-std] 11 | version = "1.6" 12 | features = ["attributes"] 13 | -------------------------------------------------------------------------------- /web服务器/异步多线程_web服务器/hello.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Hello! 7 | 8 | 9 | 10 |

Hello!

11 |

Hi from Rust

12 | 13 | 14 | -------------------------------------------------------------------------------- /web服务器/异步多线程_web服务器/src/main.rs: -------------------------------------------------------------------------------- 1 | use async_std::net::TcpListener; 2 | // use async_std::net::TcpStream; 3 | use async_std::prelude::*; 4 | use futures::stream::StreamExt; 5 | 6 | // 之前的例子有一个致命的缺陷:只能使用一个线程并发的处理用户请求。 7 | // 是的,这样也可以实现并发,一秒处理几千次请求问题不大, 8 | // 但是这毕竟没有利用上 CPU 的多核并行能力,无法实现性能最大化。 9 | 10 | // async 并发和多线程其实并不冲突,而 async-std 包也允许我们使用多个线程去处理, 11 | // 由于 handle_connection 实现了 Send 特征且不会阻塞, 12 | // 因此使用 async_std::task::spawn 是非常安全的。 13 | 14 | #[async_std::main] 15 | async fn main() { 16 | let listener = TcpListener::bind("127.0.0.1:7878").await.unwrap(); 17 | listener 18 | .incoming() 19 | .for_each_concurrent(/* limit */ None, |stream| async move { 20 | let stream = stream.unwrap(); 21 | // 至此,我们实现了同时使用并行(多线程)和并发( `async` )来同时处理多个请求! 22 | async_std::task::spawn(handle_connection(stream)); 23 | }) 24 | .await; 25 | } 26 | 27 | // async fn handle_connection(mut stream: TcpStream) 28 | // 为了方便测试, 我们修改 handle_connection 的函数签名让测试更简单。 29 | // 之所以可以修改签名,原因在于 async_std::net::TcpStream 实际上并不是必须的, 30 | // 只要任何结构体实现了 async_std::io::Read, async_std::io::Write 和 marker::Unpin 就可以替代它。 31 | use async_std::io::{Read, Write}; 32 | async fn handle_connection(mut stream: impl Read + Write + Unpin) { 33 | let mut buffer = [0; 1024]; 34 | stream.read(&mut buffer).await.unwrap(); 35 | 36 | let get = b"GET / HTTP/1.1\r\n"; 37 | let sleep = b"GET /sleep HTTP/1.1\r\n"; 38 | 39 | let (status_line, filename) = if buffer.starts_with(get) { 40 | ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") 41 | } else if buffer.starts_with(sleep) { 42 | async_std::task::sleep(std::time::Duration::from_secs(5)).await; 43 | ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") 44 | } else { 45 | ("HTTP/1.1 404 NOT FOUND\r\n\r\n", "404.html") 46 | }; 47 | let contents = std::fs::read_to_string(filename).unwrap(); 48 | 49 | let response = format!("{status_line}{contents}"); 50 | stream.write(response.as_bytes()).await.unwrap(); 51 | stream.flush().await.unwrap(); 52 | } 53 | 54 | use futures::io::Error; 55 | use futures::task::{Context, Poll}; 56 | use std::cmp::min; 57 | use std::pin::Pin; 58 | // 下面,来构建一个 mock 的 `TcpStream` 并实现了上面这些特征, 59 | // 它包含一些数据,这些数据将被拷贝到 `read` 缓存中, 然后返回 `Poll::Ready` 说明 `read` 已经结束: 60 | struct MockTcpStream { 61 | read_data: Vec, 62 | write_data: Vec, 63 | } 64 | 65 | impl Read for MockTcpStream { 66 | fn poll_read( 67 | self: Pin<&mut Self>, 68 | _: &mut Context, 69 | buf: &mut [u8], 70 | ) -> Poll> { 71 | let size: usize = min(self.read_data.len(), buf.len()); 72 | buf[..size].copy_from_slice(&self.read_data[..size]); 73 | Poll::Ready(Ok(size)) 74 | } 75 | } 76 | impl Write for MockTcpStream { 77 | fn poll_write( 78 | mut self: Pin<&mut Self>, 79 | _: &mut Context, 80 | buf: &[u8], 81 | ) -> Poll> { 82 | self.write_data = Vec::from(buf); 83 | 84 | Poll::Ready(Ok(buf.len())) 85 | } 86 | 87 | fn poll_flush(self: Pin<&mut Self>, _: &mut Context) -> Poll> { 88 | Poll::Ready(Ok(())) 89 | } 90 | 91 | fn poll_close(self: Pin<&mut Self>, _: &mut Context) -> Poll> { 92 | Poll::Ready(Ok(())) 93 | } 94 | } 95 | 96 | #[async_std::test] 97 | async fn test_handle_connection() { 98 | let input_bytes = b"GET / HTTP/1.1\r\n"; 99 | let mut contents = vec![0u8; 1024]; 100 | contents[..input_bytes.len()].clone_from_slice(input_bytes); 101 | 102 | let mut stream = MockTcpStream { 103 | read_data: contents, 104 | write_data: Vec::new(), 105 | }; 106 | 107 | handle_connection(&mut stream).await; 108 | 109 | let expected_contents = std::fs::read_to_string("hello.html").unwrap(); 110 | let expected_response = format!("HTTP/1.1 200 OK\r\n\r\n{}", expected_contents); 111 | assert!(stream.write_data.starts_with(expected_response.as_bytes())); 112 | } 113 | -------------------------------------------------------------------------------- /world_hello/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /world_hello/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "world_hello" 7 | version = "0.1.0" 8 | -------------------------------------------------------------------------------- /world_hello/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "world_hello" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /world_hello/src/main.rs: -------------------------------------------------------------------------------- 1 | fn greet_world() { 2 | // Rust 原生支持 UTF-8 编码的字符串 3 | let southern_germany = "Grüß Gott!"; 4 | let chinese = "世界,你好"; 5 | // let english = "World, hello"; 6 | let english = "World, hello"; 7 | 8 | // Rust为数组实现了IntoIterator trait,我们可以直接对一个数组进行迭代 9 | let regions = [southern_germany, chinese, english]; 10 | for region in regions { 11 | // !是一个格式化宏,用于格式化字符串 12 | // 对于 `println` 来说,我们没有使用其它语言惯用的 `%s`、`%d` 来做输出占位符,而是使用 `{}`, 13 | // 因为 Rust 在底层帮我们做了大量工作,会自动识别输出数据的类型 14 | println!("{}", ®ion); 15 | } 16 | } 17 | 18 | fn main() { 19 | greet_world(); 20 | } 21 | -------------------------------------------------------------------------------- /world_hello2/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /world_hello2/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "world_hello2" 7 | version = "0.1.0" 8 | -------------------------------------------------------------------------------- /world_hello2/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "world_hello2" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /world_hello2/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let penguin_data = "\ 3 | common name,length (cm) 4 | Little penguin,33 5 | Yellow-eyed penguin,65 6 | Fiordland penguin,60 7 | Invalid,data 8 | "; 9 | println!("{}", penguin_data); 10 | assert!(penguin_data.lines().count() == 6); 11 | assert!(penguin_data[..1].lines().count() == 1); 12 | 13 | // lines() 方法返回一个迭代器,每次迭代返回一行 14 | let records = penguin_data.lines(); 15 | 16 | // enumerate() 方法返回一个迭代器,每次迭代返回一个元组,元组的第一个元素是索引,第二个元素是迭代器中的值 17 | for (i, record) in records.enumerate() { 18 | // 跳过第一行,trim() 方法用来去掉字符串首尾的空白字符 19 | if i == 0 || record.trim().len() == 0 { 20 | continue; 21 | } 22 | 23 | // 声明一个 fields 变量,类型是 Vec 24 | // Vec 是 vector 的缩写,是一个可伸缩的集合类型,可以认为是一个动态数组 25 | // <_>表示 Vec 中的元素类型由编译器自行推断,在很多场景下,都会帮我们省却不少功夫 26 | let fields: Vec<_> = record.split(',').map(|field| field.trim()).collect(); 27 | // cfg!() 是一个宏,用来判断当前的编译配置,如果是 debug 模式,则输出调试信息 28 | if cfg!(debug_assertions) { 29 | // 输出到标准错误输出, {:?} 是一个格式化参数,用来输出 fields 变量的值 30 | eprintln!("debug: {:?} -> {:?}", record, fields); 31 | } 32 | 33 | let name = fields[0]; 34 | // 1. 尝试把 fields[1] 的值转换为 f32 类型的浮点数,如果成功,则把 f32 值赋给 length 变量 35 | // 36 | // 2. if let 是一个匹配表达式,用来从=右边的结果中,匹配出 length 的值: 37 | // 1)当=右边的表达式执行成功,则会返回一个 Ok(f32) 的类型,若失败,则会返回一个 Err(e) 类型,if let 的作用就是仅匹配 Ok 也就是成功的情况,如果是错误,就直接忽略 38 | // 2)同时 if let 还会做一次解构匹配,通过 Ok(length) 去匹配右边的 Ok(f32),最终把相应的 f32 值赋给 length 39 | // 40 | // 3. 当然你也可以忽略成功的情况,用 if let Err(e) = fields[1].parse::() {...}匹配出错误,然后打印出来,但是没啥卵用 41 | if let Ok(length) = fields[1].parse::() { 42 | // 输出到标准输出 43 | println!("{}, {}cm", name, length); 44 | } 45 | } 46 | } 47 | 48 | // 上面代码中,值得注意的 Rust 特性有: 49 | 50 | // - 控制流:`for` 和 `continue` 连在一起使用,实现循环控制。 51 | // - 方法语法:由于 Rust 没有继承,因此 Rust 不是传统意义上的面向对象语言, 52 | // 但是它却从 `OO` 语言那里偷师了方法的使用 `record.trim()`,`record.split(',')` 等。 53 | // - 高阶函数编程:函数可以作为参数也能作为返回值,例如 `.map(|field| field.trim())`, 54 | // 这里 `map` 方法中使用闭包函数作为参数,也可以称呼为 `匿名函数`、`lambda 函数`。 55 | // - 类型标注:`if let Ok(length) = fields[1].parse::()`,通过 `::` 的使用, 56 | // 告诉编译器 `length` 是一个 `f32` 类型的浮点数。这种类型标注不是很常用, 57 | // 但是在编译器无法推断出你的数据类型时,就很有用了。 58 | // - 条件编译:`if cfg!(debug_assertions)`,说明紧跟其后的输出(打印)只在 `debug` 模式下生效。 59 | // - 隐式返回:Rust 提供了 `return` 关键字用于函数返回,但是在很多时候,我们可以省略它。 60 | // 因为 Rust 是 [**基于表达式的语言**](https://course.rs/basic/base-type/statement-expression.html)。 61 | -------------------------------------------------------------------------------- /基本数据类型/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /基本数据类型/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "autocfg" 7 | version = "1.1.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 10 | 11 | [[package]] 12 | name = "lazy_static" 13 | version = "1.4.0" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 16 | 17 | [[package]] 18 | name = "num" 19 | version = "0.4.0" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "43db66d1170d347f9a065114077f7dccb00c1b9478c89384490a3425279a4606" 22 | dependencies = [ 23 | "num-bigint", 24 | "num-complex", 25 | "num-integer", 26 | "num-iter", 27 | "num-rational", 28 | "num-traits", 29 | ] 30 | 31 | [[package]] 32 | name = "num-bigint" 33 | version = "0.4.3" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" 36 | dependencies = [ 37 | "autocfg", 38 | "num-integer", 39 | "num-traits", 40 | ] 41 | 42 | [[package]] 43 | name = "num-complex" 44 | version = "0.4.3" 45 | source = "registry+https://github.com/rust-lang/crates.io-index" 46 | checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d" 47 | dependencies = [ 48 | "num-traits", 49 | ] 50 | 51 | [[package]] 52 | name = "num-integer" 53 | version = "0.1.45" 54 | source = "registry+https://github.com/rust-lang/crates.io-index" 55 | checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" 56 | dependencies = [ 57 | "autocfg", 58 | "num-traits", 59 | ] 60 | 61 | [[package]] 62 | name = "num-iter" 63 | version = "0.1.43" 64 | source = "registry+https://github.com/rust-lang/crates.io-index" 65 | checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" 66 | dependencies = [ 67 | "autocfg", 68 | "num-integer", 69 | "num-traits", 70 | ] 71 | 72 | [[package]] 73 | name = "num-rational" 74 | version = "0.4.1" 75 | source = "registry+https://github.com/rust-lang/crates.io-index" 76 | checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" 77 | dependencies = [ 78 | "autocfg", 79 | "num-bigint", 80 | "num-integer", 81 | "num-traits", 82 | ] 83 | 84 | [[package]] 85 | name = "num-traits" 86 | version = "0.2.15" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" 89 | dependencies = [ 90 | "autocfg", 91 | ] 92 | 93 | [[package]] 94 | name = "基本数据类型" 95 | version = "0.1.0" 96 | dependencies = [ 97 | "lazy_static", 98 | "num", 99 | ] 100 | -------------------------------------------------------------------------------- /基本数据类型/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "基本数据类型" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | lazy_static = "1.4.0" 10 | num = "0.4.0" 11 | -------------------------------------------------------------------------------- /复合类型/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /复合类型/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "复合类型" 7 | version = "0.1.0" 8 | -------------------------------------------------------------------------------- /复合类型/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "复合类型" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /复合类型/src/array.rs: -------------------------------------------------------------------------------- 1 | pub fn study_array() { 2 | println!("-----------------数组-----------------"); 3 | // 在 Rust 中,最常用的数组有两种,第一种是速度很快但是长度固定的 `array`, 4 | // 第二种是可动态增长的但是有性能损耗的 `Vector` 类型。 5 | // 这两个数组的关系跟 `&str` 与 `String` 的关系很像,前者是长度固定的字符串切片,后者是可动态增长的字符串。 6 | // 其实,在 Rust 中无论是 `String` 还是 `Vector`,它们都是 Rust 的高级类型:集合类型。 7 | // 数组 `array` 是存储在栈上,性能也会非常优秀。与此对应,动态数组 `Vector` 是存储在堆上。 8 | println!("-----------------创建数组-----------------"); 9 | let a = [1, 2, 3, 4, 5]; 10 | let b: [i32; 5] = [1, 2, 3, 4, 5]; 11 | println!("the first element of a is: {}", a[0]); 12 | if a == b { 13 | // 数组默认实现了 PartialEq trait,因此可以直接使用 == 进行比较 14 | println!("a == b"); 15 | } 16 | let c = [0; 5]; // 创建一个长度为 5 的数组,每个元素都是 0 17 | println!("c = {:?}", c); 18 | println!("-----------------数组的遍历-----------------"); 19 | // 1. for 循环 20 | for i in 0..a.len() { 21 | print!("{} ", a[i]); 22 | } 23 | println!(); 24 | // 2. for_each 25 | a.iter().for_each(|x| print!("{} ", x)); 26 | println!(); 27 | // 3. for in 28 | for x in a.iter() { 29 | print!("{} ", x); 30 | } 31 | println!(); 32 | // 4. for in &a 33 | for x in &a { 34 | print!("{} ", x); 35 | } 36 | println!(); 37 | // 数组元素为非基础类型 38 | println!("-----------------数组元素为非基础类型-----------------"); 39 | // 前面几个例子都是Rust的基本类型,而基本类型在Rust中赋值是以Copy的形式,这时候你就懂了吧, 40 | // let array=[3;5]底层就是不断的Copy出来的。而如果数组元素是非基础类型,那么就会出现所有权的问题。 41 | // let array = [String::from("rust is good!"); 8]; // error 42 | // 正确的写法 43 | let array: [String; 8] = std::array::from_fn(|_i| String::from("rust is good!")); 44 | println!("array = {:?}", array); 45 | 46 | // 数组切片 47 | // 创建切片的代价非常小,因为切片只是针对底层数组的一个引用。 48 | println!("-----------------数组切片-----------------"); 49 | let a: [i32; 5] = [1, 2, 3, 4, 5]; 50 | let slice: &[i32] = &a[1..3]; 51 | println!("slice = {:?}", slice); 52 | assert_eq!(slice, &[2, 3]); 53 | // 也可以使用 .. 运算符来创建一个包含数组中所有元素的切片 54 | let slice: &[i32] = &a[..]; 55 | println!("slice = {:?}", slice); 56 | assert_eq!(slice, &[1, 2, 3, 4, 5]); 57 | 58 | // 数组切片可以直接和数组进行比较 59 | println!("-----------------数组切片可以直接和数组进行比较-----------------"); 60 | let a: [i32; 5] = [1, 2, 3, 4, 5]; 61 | let slice: &[i32] = &[1, 2, 3, 4, 5]; 62 | if a == slice { 63 | // 长度相同,元素相同 64 | println!("{:?} == {:?}", a, slice); 65 | } else { 66 | println!("{:?} != {:?}", a, slice); 67 | } 68 | 69 | // 从字符串字面量创建切片 70 | println!("-----------------从字符串字面量创建切片-----------------"); 71 | let a: &[u8] = b"hello"; //b means byte 72 | println!("a = {:?}", a); 73 | assert_eq!(a, &[b'h', b'e', b'l', b'l', b'o']); 74 | 75 | // 从vector创建数组切片 76 | println!("-----------------从vector创建切片-----------------"); 77 | let v = vec![111, 222, 333, 444, 555]; 78 | let slice = &v[1..3]; 79 | println!("slice = {:?}", slice); 80 | assert_eq!(slice, &[222, 333]); 81 | 82 | // 从 Vector 创建数组切片 83 | let v = vec![111, 222, 333, 444, 555]; 84 | let slice = v.as_slice(); 85 | println!("slice = {:?}", slice); 86 | assert_eq!(slice, &[111, 222, 333, 444, 555]); 87 | 88 | // 切片可以是对数组的可变引用 89 | println!("-----------------切片可以是对数组的可变引用-----------------"); 90 | let mut b: [i32; 5] = [1, 2, 3, 4, 5]; 91 | let slice: &mut [i32] = &mut b[1..3]; 92 | println!("slice = {:?}", slice); 93 | assert_eq!(slice, &[2, 3]); 94 | slice[0] = 10; 95 | slice[1] = 20; 96 | println!("slice = {:?}", slice); 97 | println!("b = {:?}", b); 98 | 99 | // 切片引用 和 数组引用 的区别 100 | // &[u8] 和 &[u8; N] 的区别 101 | println!("-----------------切片引用和数组引用的区别-----------------"); 102 | // &[u8] 是切片的引用(平常所说的切片就是指切片的引用),包含一个长度信息。 103 | // &[u8; N] 是一个 u8数组 的引用,数组是固定长度的,引用不需要包含长度信息。 104 | let a: [u8; 5] = [1, 2, 3, 4, 5]; 105 | let _slice: &[u8] = &a[..]; 106 | println!("sizeof(&[u8]) = {}", std::mem::size_of::<&[u8]>()); // 16 107 | 108 | let a: [u8; 5] = [1, 2, 3, 4, 5]; 109 | let _slice: &[u8; 5] = &a; 110 | println!("sizeof(&[u8; 5]) = {}", std::mem::size_of::<&[u8; 5]>()); // 8 111 | } 112 | -------------------------------------------------------------------------------- /复合类型/src/enumeration.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug)] 2 | enum PokerSuit { 3 | _Clubs, // 0 4 | _Spades, // 1 5 | _Diamonds, // 2 6 | _Hearts, // 3 7 | } 8 | pub fn study_enumeration() { 9 | println!("-----------------枚举-----------------"); 10 | let heart = PokerSuit::_Hearts; 11 | let diamond = PokerSuit::_Diamonds; 12 | print_suit(heart); 13 | print_suit(diamond); 14 | // 将数据信息关联到枚举成员上 15 | println!("-----------------将数据信息关联到枚举成员上-----------------"); 16 | let c1 = PokerCard::_Spades(5); //花色为黑桃,点数为5 17 | let c2 = PokerCard::_Diamonds(13); //花色为方块,点数为13 18 | println!("{:?}", c1); 19 | println!("{:?}", c2); 20 | // 任何类型的数据都可以放入枚举成员中 21 | println!("-----------------任何类型的数据都可以放入枚举成员中-----------------"); 22 | let m1 = Message::_Quit; 23 | let m2 = Message::_Move { _x: 1, _y: 2 }; 24 | println!("{:?}", m1); 25 | println!("{:?}", m2); 26 | // Option 枚举用于处理空值 27 | println!("-----------------Option 枚举用于处理空值-----------------"); 28 | // `Option` 枚举是如此有用以至于它被包含在了 prelude 之中, 29 | // prelude 属于 Rust 标准库,Rust 会将最常用的类型、函数等提前引入其中,省得我。们再手动引入, 30 | // 你不需要将其显式引入作用域。另外,它的成员 `Some` 和 `None` 也是如此 31 | let some_number = Some(5); 32 | let some_string = Some("a string"); 33 | // 如果使用 `None` 而不是 `Some`,需要告诉 Rust `Option` 是什么类型的, 34 | // 因为编译器只通过 `None` 值无法推断出 `Some` 成员保存的值的类型。 35 | let absent_number: Option = None; 36 | println!("{:?}, {:?}, {:?}", some_number, some_string, absent_number); 37 | // 当在 Rust 中拥有一个像 `i8` 这样类型的值时,编译器确保它总是有一个有效的值, 38 | // 我们可以放心使用而无需做空值检查。只有当使用 `Option`(或者任何用到的类型)的时候 39 | // 才需要担心可能没有值,而编译器会确保我们在使用值之前处理了为空的情况。 40 | 41 | // 用 `match` 表达式处理枚举 42 | println!("-----------------用 `match` 表达式处理枚举-----------------"); 43 | let some_u8_value = 0u8; 44 | match some_u8_value { 45 | 1 => println!("one"), 46 | 3 => println!("three"), 47 | 5 => println!("five"), 48 | 7 => println!("seven"), 49 | _ => println!("other"), 50 | } 51 | // 用 `if let` 表达式处理枚举 52 | println!("-----------------用 `if let` 表达式处理枚举-----------------"); 53 | let some_u8_value = Some(0u8); 54 | if let Some(3) = some_u8_value { 55 | println!("three"); 56 | } 57 | } 58 | 59 | fn print_suit(card: PokerSuit) { 60 | println!("{:?}", card); 61 | } 62 | 63 | #[derive(Debug)] 64 | enum PokerCard { 65 | _Clubs(u8), 66 | _Spades(u8), 67 | _Diamonds(u8), 68 | _Hearts(u8), 69 | } 70 | 71 | #[derive(Debug)] 72 | enum Message { 73 | // Rust 会按照枚举中占用内存最大的那个成员进行内存对齐,这意味着可能造成内存上的浪费。 74 | _Quit, 75 | _Move { _x: i32, _y: i32 }, 76 | _Write(String), 77 | _ChangeColor(i32, i32, i32), 78 | } 79 | /* 80 | - `Quit` 没有任何关联数据 81 | - `Move` 包含一个匿名结构体 82 | - `Write` 包含一个 `String` 字符串 83 | - `ChangeColor` 包含三个 `i32` 84 | 85 | 如果用结构体的方式来定义这些消息: 86 | struct QuitMessage; // 单元结构体 87 | struct MoveMessage { 88 | x: i32, 89 | y: i32, 90 | } 91 | struct WriteMessage(String); // 元组结构体 92 | struct ChangeColorMessage(i32, i32, i32); // 元组结构体 93 | 94 | 由于每个结构体都有自己的类型,因此我们无法在需要同一类型的地方进行使用, 95 | 例如某个函数它的功能是接受消息并进行发送,那么用枚举的方式,就可以接收不同的消息, 96 | 但是用结构体,该函数无法接受 4 个不同的结构体作为参数。 97 | */ 98 | -------------------------------------------------------------------------------- /复合类型/src/sort.rs: -------------------------------------------------------------------------------- 1 | pub fn study_sort() { 2 | println!("-----------------排序-----------------"); 3 | let mut vec = vec![1, 5, 10, 2, 15]; 4 | // 升序 5 | vec.sort(); 6 | println!("vec = {:?}", vec); 7 | // 降序 8 | vec.sort_by(|a, b| b.cmp(a)); 9 | println!("vec = {:?}", vec); 10 | 11 | // unstable sort 12 | vec.sort_unstable(); 13 | println!("vec = {:?}", vec); 14 | 15 | // sort_by_key 16 | let mut a = [-5i32, 4, 1, -3, 2]; 17 | a.sort_by_key(|k| k.abs()); 18 | println!("array = {:?}", a); 19 | 20 | // 对浮点数排序 21 | let mut vec = vec![1.1, 1.15, 5.5, 1.123, 2.0]; 22 | vec.sort_by(|a, b| a.partial_cmp(b).unwrap()); 23 | println!("vec = {:?}", vec); 24 | 25 | // 排序结构的 vector 26 | // 对 Person 结构的 Vector 进行排序,通过属性name和age的自然顺序(按名称和年龄)。 27 | // 为了使 Person 可排序,你需要四个 traitEq,PartialEq,Ord和PartialOrd。 28 | // 可以简单地derive出这些特征。您还可以使用一个vec:sort_by方法,提供自定义比较函数:只按年龄排序。 29 | println!("-----------------排序结构的 vector-----------------"); 30 | 31 | #[derive(Debug, Eq, Ord, PartialEq, PartialOrd)] 32 | struct Person { 33 | name: String, 34 | age: u32, 35 | } 36 | 37 | impl Person { 38 | pub fn new(name: String, age: u32) -> Self { 39 | Person { name, age } 40 | } 41 | } 42 | 43 | let mut people = vec![ 44 | Person::new("Zoe".to_string(), 25), 45 | Person::new("Al".to_string(), 60), 46 | Person::new("John".to_string(), 1), 47 | ]; 48 | 49 | // 自然顺序,排序 people (名字 和 年龄) 50 | people.sort(); 51 | 52 | for p in &people { 53 | println!("{:?}", p); 54 | } 55 | println!("-----------------排序结构的 vector-----------------"); 56 | // 用 年龄 排序 57 | people.sort_by(|a, b| b.age.cmp(&a.age)); 58 | 59 | for p in &people { 60 | println!("{:?}", p); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /复合类型/src/struct1.rs: -------------------------------------------------------------------------------- 1 | struct User { 2 | active: bool, 3 | username: String, 4 | email: String, 5 | sign_in_count: u64, 6 | } 7 | 8 | // implement `std::fmt::Display` for `User` 9 | impl std::fmt::Display for User { 10 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 11 | write!( 12 | f, 13 | "User {{ active: {}, username: {}, email: {}, sign_in_count: {} }}", 14 | self.active, self.username, self.email, self.sign_in_count 15 | ) 16 | } 17 | } 18 | 19 | fn build_user(email: String, username: String) -> User { 20 | // 简化结构体初始化 21 | // 当函数参数和结构体字段同名时,可以直接使用缩略的方式进行初始化 22 | User { 23 | email, 24 | username, 25 | active: true, 26 | sign_in_count: 1, 27 | } 28 | } 29 | 30 | pub fn study_struct() { 31 | println!("-----------------结构体-----------------"); 32 | println!("-----------------定义结构体-----------------"); 33 | let mut user1 = User { 34 | email: String::from("someone@example.com"), 35 | username: String::from("someusername123"), 36 | active: true, 37 | sign_in_count: 1, 38 | }; 39 | user1.email = String::from("anotheremail@example.com"); 40 | println!("{}", user1); 41 | // 简化结构体初始化 42 | let user2 = build_user( 43 | "someone@example.com".to_string(), 44 | "someusername123".to_string(), 45 | ); 46 | println!("{}", user2); 47 | // 根据已有的结构体实例,创建新的结构体实例 48 | println!("-----------------根据已有的结构体实例,创建新的结构体实例-----------------"); 49 | // 从 user1 实例中创建一个新的 user3 实例,只是将 email 字段的值改为了另一个值 50 | let user3 = User { 51 | email: String::from("xxxeample.com"), 52 | ..user1 53 | }; 54 | println!("{}", user3); 55 | // 注意在上面的`username` 字段发生了所有权转移,作为结果,`user1.username` 不再有效 56 | //println!("{}", user1); // error: value borrowed here after move 57 | //虽然user1.username不再有效,但是其他字段还是有效的 58 | println!("user1.email = {}", user1.email); 59 | 60 | // 元组结构体(Tuple Struct) 61 | println!("-----------------元组结构体(Tuple Struct)-----------------"); 62 | // 结构体必须要有名称,但是结构体的字段可以没有名称,这种结构体长得很像元组,因此被称为元组结构体。 63 | struct Color(i32, i32, i32); 64 | struct Point(i32, i32, i32); 65 | 66 | let _black = Color(0, 0, 0); 67 | let _origin = Point(0, 0, 0); 68 | // 单元结构体(Unit-like Struct) 69 | println!("-----------------单元结构体(Unit-like Struct)-----------------"); 70 | // 还记得之前讲过的基本没啥用的单元类型吧?单元结构体就跟它很像,没有任何字段和属性,但是好在,它还挺有用。 71 | // 如果你定义一个类型,但是不关心该类型的内容, 只关心它的行为时,就可以使用 `单元结构体`。 72 | let subject = AlwaysEqual; 73 | println!("{}", subject); 74 | 75 | // 使用 `#[derive(Debug)\]` 来打印结构体的信息(使用 derive 派生实现) 76 | println!("-----------------使用 `#[derive(Debug)\\]` 来打印结构体的信息-----------------"); 77 | let rect1 = Rectangle { 78 | _width: 30, 79 | _height: 50, 80 | }; 81 | println!("rect1 is {:?}", rect1); 82 | // 当结构体较大时,我们可能希望能够有更好的输出表现,此时可以使用 `{:#?}` 来替代 `{:?}`。 83 | println!("rect1 is {:#?}", rect1); 84 | 85 | // {} -> std::fmt::Display 86 | // {:?} -> std::fmt::Debug 87 | // {:#?} -> std::fmt::Debug 88 | // {:b} -> std::fmt::Binary 89 | // {:x} -> std::fmt::LowerHex 90 | // {:X} -> std::fmt::UpperHex 91 | 92 | println!("-----------------使用 `dbg!` 来打印结构体的信息-----------------"); 93 | // 还有一个简单的输出 debug 信息的方法,那就是使用 dbg! 宏, 94 | // 它会拿走表达式的所有权,然后打印出相应的文件名、行号等 debug 信息, 95 | // 当然还有我们需要的表达式的求值结果。除此之外,它最终还会把表达式值的所有权返回! 96 | // dbg!` 输出到标准错误输出 stderr。 97 | dbg!(rect1); 98 | } 99 | 100 | #[derive(Debug)] 101 | struct Rectangle { 102 | _width: u32, 103 | _height: u32, 104 | } 105 | 106 | struct AlwaysEqual; 107 | 108 | // 我们不关心 AlwaysEqual 的字段数据,只关心它的行为,因此将它声明为单元结构体,然后再为它实现某个特征 109 | impl std::fmt::Display for AlwaysEqual { 110 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 111 | write!(f, "AlwaysEqual") 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /复合类型/src/tuple.rs: -------------------------------------------------------------------------------- 1 | pub fn study_tuple() { 2 | println!("-----------------元组-----------------"); 3 | // 元组是由多种类型组合到一起形成的,因此它是复合类型,元组的长度是固定的,元组中元素的顺序也是固定的。 4 | let tup: (i32, f64, u8) = (500, 6.4, 1); 5 | dbg!(tup); 6 | // 用模式匹配解构元组 7 | println!("-----------------用模式匹配解构元组-----------------"); 8 | let (_x, y, _z) = tup; 9 | println!("The value of y is: {}", y); 10 | // 用 . 来访问元组 11 | println!("-----------------用 . 来访问元组-----------------"); 12 | let five_hundred = tup.0; 13 | let six_point_four = tup.1; 14 | let one = tup.2; 15 | println!( 16 | "five_hundred: {}, six_point_four: {}, one: {}", 17 | five_hundred, six_point_four, one 18 | ); 19 | // 函数返回多个值可以使用元组 20 | println!("-----------------函数返回多个值可以使用元组-----------------"); 21 | let (x, y) = return_tuple(); 22 | println!("x: {}, y: {}", x, y); 23 | } 24 | 25 | fn return_tuple() -> (i32, i32) { 26 | (1, 2) 27 | } 28 | -------------------------------------------------------------------------------- /宏编程/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /宏编程/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "hello_macro" 7 | version = "0.1.0" 8 | dependencies = [ 9 | "hello_macro_derive", 10 | "syn", 11 | ] 12 | 13 | [[package]] 14 | name = "hello_macro_derive" 15 | version = "0.1.0" 16 | dependencies = [ 17 | "quote", 18 | "syn", 19 | ] 20 | 21 | [[package]] 22 | name = "proc-macro2" 23 | version = "1.0.67" 24 | source = "registry+https://github.com/rust-lang/crates.io-index" 25 | checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" 26 | dependencies = [ 27 | "unicode-ident", 28 | ] 29 | 30 | [[package]] 31 | name = "quote" 32 | version = "1.0.33" 33 | source = "registry+https://github.com/rust-lang/crates.io-index" 34 | checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" 35 | dependencies = [ 36 | "proc-macro2", 37 | ] 38 | 39 | [[package]] 40 | name = "syn" 41 | version = "2.0.37" 42 | source = "registry+https://github.com/rust-lang/crates.io-index" 43 | checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8" 44 | dependencies = [ 45 | "proc-macro2", 46 | "quote", 47 | "unicode-ident", 48 | ] 49 | 50 | [[package]] 51 | name = "unicode-ident" 52 | version = "1.0.8" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" 55 | 56 | [[package]] 57 | name = "宏编程" 58 | version = "0.1.0" 59 | dependencies = [ 60 | "hello_macro", 61 | "hello_macro_derive", 62 | ] 63 | -------------------------------------------------------------------------------- /宏编程/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "宏编程" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | 10 | hello_macro = { path = "E:/Doraemon/IT/Rust/study/hello_macro" } 11 | hello_macro_derive = { path = "E:/Doraemon/IT/Rust/study/hello_macro/hello_macro_derive" } 12 | -------------------------------------------------------------------------------- /实现一个简单的redis/hello-my-redis/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /实现一个简单的redis/hello-my-redis/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "my-redis" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | # Tokio 有很多功能和特性,例如 TCP,UDP,Unix sockets,同步工具,多调度类型等等, 10 | # 不是每个应用都需要所有的这些特性。为了优化编译时间和最终生成可执行文件大小、内存占用大小, 11 | # 应用可以对这些特性进行可选引入。 12 | # 而这里为了演示的方便,我们使用 full ,表示直接引入所有的特性。 13 | tokio = { version = "1", features = ["full"] } 14 | mini-redis = "0.4" 15 | 16 | 17 | # 示例对象的文件在根目录下的 examples 目录中。既然是示例,自然是使用项目中的库对象的功能进行演示。 18 | # 示例对象编译后的文件会存储在 target/debug/examples 目录下。 19 | # 如上所示,示例对象可以使用库对象的公共 API,也可以通过 [dependencies] 来引入外部的依赖库。 20 | 21 | # 默认情况下,示例对象都是可执行的二进制文件( 带有 fn main() 函数入口), 22 | # 毕竟例子是用来测试和演示我们的库对象,是用来运行的。而你完全可以将示例对象改成库的类型: 23 | # [[example]] 24 | # name = "foo" 25 | # crate-type = ["staticlib"] 26 | # 如果想要指定运行某个示例对象,可以使用 cargo run --example 命令。 27 | # 如果是库类型的示例对象,则可以使用 cargo build --example 进行构建。 28 | # 与此类似,还可以使用 cargo install --example 来将 29 | # 示例对象编译出的可执行文件安装到默认的目录中,将该目录添加到 $PATH 环境变量中, 30 | # 就可以直接全局运行安装的可执行文件。 31 | 32 | # 最后,cargo test 命令默认会对示例对象进行编译,以防止示例代码因为长久没运行,导致严重过期以至于无法运行。 33 | [[example]] 34 | name = "hello-redis" 35 | path = "examples/hello-redis.rs" 36 | -------------------------------------------------------------------------------- /实现一个简单的redis/hello-my-redis/examples/hello-redis.rs: -------------------------------------------------------------------------------- 1 | use mini_redis::{Result, client}; 2 | 3 | // 在代码中,使用了一个与众不同的 main 函数 : async fn main , 4 | // 而且是用 #[tokio::main] 属性进行了标记。异步 main 函数有以下意义: 5 | 6 | // .await 只能在 async 函数中使用,如果是以前的 fn main, 7 | // 那它内部是无法直接使用 async 函数的!这个会极大的限制了我们的使用场景 8 | // 异步运行时本身需要初始化 9 | 10 | // `#[tokio::main]` 宏在将 `async fn main` 隐式的转换为 `fn main` 的同时 11 | // 还对整个异步运行时进行了初始化。例如以下代码: 12 | /* 13 | 14 | #[tokio::main] 15 | async fn main() { 16 | println!("hello"); 17 | } 18 | 19 | 将被转换成: 20 | fn main() { 21 | let mut rt = tokio::runtime::Runtime::new().unwrap(); 22 | rt.block_on(async { 23 | println!("hello"); 24 | }) 25 | } 26 | 27 | */ 28 | 29 | #[tokio::main] 30 | async fn main() -> Result<()> { 31 | // 建立与mini-redis服务器的连接 32 | // >$ mini-redis-server 33 | let mut client = client::connect("127.0.0.1:6379").await?; 34 | 35 | // 设置 key: "hello" 和 值: "world" 36 | client.set("hello", "world".into()).await?; 37 | 38 | // 获取"key=hello"的值 39 | let result = client.get("hello").await?; 40 | 41 | println!("从服务器端获取到结果={:?}", result); 42 | 43 | async fn say_to_world() -> String { 44 | String::from("world") 45 | } 46 | 47 | // 此处的函数调用是惰性的,并不会执行 say_to_world() 函数体中的代码 48 | let op = say_to_world(); 49 | 50 | // 首先打印出 "hello" 51 | println!("hello"); 52 | 53 | // 使用 .await 让 say_to_world 开始运行起来 54 | // async fn 到底返回什么?它实际上返回的是一个实现了 Future 特征的匿名类型: 55 | // impl Future。 56 | println!("{}", op.await); 57 | 58 | Ok(()) 59 | } 60 | -------------------------------------------------------------------------------- /实现一个简单的redis/hello-my-redis/src/main.rs: -------------------------------------------------------------------------------- 1 | use mini_redis::{Connection, Frame}; 2 | use tokio::net::{TcpListener, TcpStream}; 3 | 4 | #[tokio::main] 5 | async fn main() { 6 | // Tokio 中大多数类型的名称都和标准库中对应的同步类型名称相同,而且, 7 | // 如果没有特殊原因,Tokio 的 API 名称也和标准库保持一致,只不过用 async fn 取代 fn 来声明函数。 8 | 9 | // Bind the listener to the address 10 | // 监听指定地址,等待 TCP 连接进来 11 | let listener = TcpListener::bind("127.0.0.1:6379").await.unwrap(); 12 | 13 | loop { 14 | let (socket, _) = listener.accept().await.unwrap(); 15 | // 为每一条连接都生成一个新的任务, 16 | // socket 的所有权将被移动到新的任务中,并在那里进行处理 17 | tokio::spawn(async move { 18 | process(socket).await; 19 | }); 20 | } 21 | } 22 | 23 | async fn process(socket: TcpStream) { 24 | // Connection 对于 redis 的读写进行了抽象封装, 25 | // 因此我们读到的是一个一个数据帧frame(数据帧 = redis命令 + 数据),而不是字节流 26 | // Connection 是在 mini-redis 中定义 27 | let mut connection = Connection::new(socket); 28 | 29 | // 使用 hashmap 来存储 redis 的数据 30 | let mut db = std::collections::HashMap::new(); 31 | 32 | // if let Some(frame) = connection.read_frame().await.unwrap() { 33 | // println!("GOT: {:?}", frame); 34 | 35 | // 回复一个错误 36 | // let response = Frame::Error("unimplemented".to_string()); 37 | // connection.write_frame(&response).await.unwrap(); 38 | // } 39 | // 使用 `read_frame` 方法从连接获取一个数据帧:一条redis命令 + 相应的数据 40 | use mini_redis::Command::{Get, Set}; 41 | while let Some(frame) = connection.read_frame().await.unwrap() { 42 | let response = match mini_redis::Command::from_frame(frame).unwrap() { 43 | Set(cmd) => { 44 | // 值被存储为 `Vec` 的形式 45 | db.insert(cmd.key().to_string(), cmd.value().to_vec()); 46 | Frame::Simple("OK".to_string()) 47 | } 48 | Get(cmd) => { 49 | if let Some(value) = db.get(cmd.key()) { 50 | // `Frame::Bulk` 期待数据的类型是 `Bytes`, 该类型会在后面章节讲解, 51 | // 此时,你只要知道 `&Vec` 可以使用 `into()` 方法转换成 `Bytes` 类型 52 | Frame::Bulk(value.clone().into()) 53 | } else { 54 | Frame::Null 55 | } 56 | } 57 | cmd => panic!("unimplemented {:?}", cmd), 58 | }; 59 | 60 | // 将请求响应返回给客户端 61 | connection.write_frame(&response).await.unwrap(); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /实现一个简单的redis/mini_redis/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /实现一个简单的redis/mini_redis/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "mini_redis" 7 | version = "0.1.0" 8 | -------------------------------------------------------------------------------- /实现一个简单的redis/mini_redis/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mini_redis" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /实现一个简单的redis/mini_redis/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("Hello, world!"); 3 | } 4 | 5 | // 虽然 tokio 对于大多数需要并发的项目都是非常适合的,但是确实有一些场景它并不适合使用: 6 | // - 并行运行 CPU 密集型的任务。tokio 非常适合于 IO 密集型任务, 7 | // 这些 IO 任务的绝大多数时间都用于阻塞等待 IO 的结果,而不是刷刷刷的单烤 CPU。 8 | // 如果你的应用是 CPU 密集型(例如并行计算),建议使用 rayon, 9 | // 当然,对于其中的 IO 任务部分,你依然可以混用 tokio 10 | // - 读取大量的文件。读取文件的瓶颈主要在于操作系统,因为 OS 没有提供异步文件读取接口, 11 | // 大量的并发并不会提升文件读取的并行性能,反而可能会造成不可忽视的性能损耗, 12 | // 因此建议使用线程(或线程池)的方式 13 | // - 发送少量 HTTP 请求。tokio 的优势是给予你并发处理大量任务的能力, 14 | // 对于这种轻量级 HTTP 请求场景,tokio 除了增加你的代码复杂性,并无法带来什么额外的优势。 15 | // 因此,对于这种场景,你可以使用 reqwest库,它会更加简单易用。 16 | 17 | // 若大家使用 tokio,那 CPU 密集的任务尤其需要用线程的方式去处理, 18 | // 例如使用 spawn_blocking 创建一个阻塞的线程去完成相应 CPU 密集任务。 19 | // 原因是:tokio 是协作式的调度器,如果某个 CPU 密集的异步任务是通过 tokio 创建的, 20 | // 那理论上来说,该异步任务需要跟其它的异步任务交错执行,最终大家都得到了执行,皆大欢喜。 21 | // 但实际情况是,CPU 密集的任务很可能会一直霸着着 CPU,此时 tokio 的调度方式决定了该任务会一直被执行, 22 | // 这意味着,其它的异步任务无法得到执行的机会,最终这些任务都会因为得不到资源而饿死。 23 | // 而使用 spawn_blocking 后,会创建一个单独的 OS 线程,该线程并不会被 tokio 所调度( 被 OS 所调度 ), 24 | // 因此它所执行的 CPU 密集任务也不会导致 tokio 调度的那些异步任务被饿死 25 | -------------------------------------------------------------------------------- /实现一个简单的redis/my-redis-stream/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /实现一个简单的redis/my-redis-stream/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "my-redis-stream" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | tokio = { version = "1", features = ["full"] } 10 | tokio-stream = "0.1" 11 | mini-redis = "0.4" 12 | -------------------------------------------------------------------------------- /实现一个简单的redis/my-redis-stream/src/bin/client.rs: -------------------------------------------------------------------------------- 1 | use mini_redis::client; 2 | use tokio_stream::StreamExt; 3 | 4 | async fn publish() -> mini_redis::Result<()> { 5 | let mut client = client::connect("127.0.0.1:6379").await?; 6 | 7 | // 发布一些数据 8 | client.publish("numbers", "1".into()).await?; 9 | client.publish("numbers", "two".into()).await?; 10 | client.publish("numbers", "3".into()).await?; 11 | client.publish("numbers", "four".into()).await?; 12 | client.publish("numbers", "five".into()).await?; 13 | client.publish("numbers", "6".into()).await?; 14 | Ok(()) 15 | } 16 | 17 | async fn subscribe() -> mini_redis::Result<()> { 18 | let client = client::connect("127.0.0.1:6379").await?; 19 | let subscriber = client.subscribe(vec!["numbers".to_string()]).await?; 20 | // into_stream 会将 `Subscriber` 变成一个 `stream` 21 | let messages = subscriber.into_stream(); 22 | 23 | tokio::pin!(messages); 24 | 25 | while let Some(msg) = messages.next().await { 26 | println!("got = {:?}", msg); 27 | } 28 | 29 | Ok(()) 30 | } 31 | 32 | // 上面生成了一个异步任务专门用于发布消息到 min-redis 服务器端的 `numbers` 消息通道中。 33 | // 然后,在 `main` 中,我们订阅了 `numbers` 消息通道,并且打印从中接收到的消息。 34 | 35 | // 先运行mini-redis服务器 36 | // $mini-redis-server 37 | #[tokio::main] 38 | async fn main() -> mini_redis::Result<()> { 39 | tokio::spawn(async { publish().await }); 40 | 41 | subscribe().await?; 42 | 43 | println!("DONE"); 44 | 45 | Ok(()) 46 | } 47 | -------------------------------------------------------------------------------- /实现一个简单的redis/my-redis-共享状态/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /实现一个简单的redis/my-redis-共享状态/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "my-redis2" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | tokio = { version = "1", features = ["full"] } 10 | mini-redis = "0.4" 11 | bytes = "1" 12 | 13 | 14 | [[example]] 15 | name = "hello-redis" 16 | path = "examples/hello-redis.rs" 17 | -------------------------------------------------------------------------------- /实现一个简单的redis/my-redis-共享状态/examples/hello-redis.rs: -------------------------------------------------------------------------------- 1 | use mini_redis::{Result, client}; 2 | 3 | // 在代码中,使用了一个与众不同的 main 函数 : async fn main , 4 | // 而且是用 #[tokio::main] 属性进行了标记。异步 main 函数有以下意义: 5 | 6 | // .await 只能在 async 函数中使用,如果是以前的 fn main, 7 | // 那它内部是无法直接使用 async 函数的!这个会极大的限制了我们的使用场景 8 | // 异步运行时本身需要初始化 9 | 10 | // `#[tokio::main]` 宏在将 `async fn main` 隐式的转换为 `fn main` 的同时 11 | // 还对整个异步运行时进行了初始化。例如以下代码: 12 | /* 13 | 14 | #[tokio::main] 15 | async fn main() { 16 | println!("hello"); 17 | } 18 | 19 | 将被转换成: 20 | fn main() { 21 | let mut rt = tokio::runtime::Runtime::new().unwrap(); 22 | rt.block_on(async { 23 | println!("hello"); 24 | }) 25 | } 26 | 27 | */ 28 | 29 | #[tokio::main] 30 | async fn main() -> Result<()> { 31 | // 建立与mini-redis服务器的连接 32 | // >$ mini-redis-server 33 | let mut client = client::connect("127.0.0.1:6379").await?; 34 | 35 | // 设置 key: "hello" 和 值: "world" 36 | client.set("hello", "world".into()).await?; 37 | 38 | // 获取"key=hello"的值 39 | let result = client.get("hello").await?; 40 | 41 | println!("从服务器端获取到结果={:?}", result); 42 | 43 | async fn say_to_world() -> String { 44 | String::from("world") 45 | } 46 | 47 | // 此处的函数调用是惰性的,并不会执行 say_to_world() 函数体中的代码 48 | let op = say_to_world(); 49 | 50 | // 首先打印出 "hello" 51 | println!("hello"); 52 | 53 | // 使用 .await 让 say_to_world 开始运行起来 54 | // async fn 到底返回什么?它实际上返回的是一个实现了 Future 特征的匿名类型: 55 | // impl Future。 56 | println!("{}", op.await); 57 | 58 | Ok(()) 59 | } 60 | -------------------------------------------------------------------------------- /实现一个简单的redis/my-redis-共享状态/src/bin/client.rs: -------------------------------------------------------------------------------- 1 | use bytes::Bytes; 2 | use mini_redis::client; 3 | use tokio::sync::{mpsc, oneshot}; 4 | 5 | // 我们的例子中,任务需要将 `GET` 和 `SET` 命令处理的结果返回。 6 | // 首先,我们需要定一个 `Command` 枚举用于代表命令: 7 | /// Multiple different commands are multiplexed over a single channel. 8 | #[derive(Debug)] 9 | enum Command { 10 | Get { 11 | key: String, 12 | resp: Responder>, 13 | }, 14 | Set { 15 | key: String, 16 | val: Bytes, 17 | resp: Responder<()>, 18 | }, 19 | } 20 | 21 | /// Provided by the requester and used by the manager task to send the command 22 | /// response back to the requester. 23 | type Responder = oneshot::Sender>; 24 | 25 | #[tokio::main] 26 | async fn main() { 27 | // Tokio 提供了多种消息通道,可以满足不同场景的需求: 28 | // - mpsc, 多生产者,单消费者模式 29 | // - oneshot, 单生产者单消费,只能发送一次消息 30 | // - broadcast,多生产者,多消费者,其中每一条发送的消息都可以被所有接收者收到,因此是广播 31 | // - watch,单生产者,多消费者,只保存一条最新的消息,因此接收者只能看到最近的一条消息, 32 | // 例如,这种模式适用于配置文件变化的监听 33 | // 这里还少了一种类型:多生产者、多消费者(包括单生产者,多消费者),且每一条消息只能被其中一个消费者接收, 34 | // 如果有这种需求,可以使用 async-channel 包。 35 | // 在多线程章节中提到过的 std::sync::mpsc 和 crossbeam::channel, 36 | // 这些通道在等待消息时会阻塞当前的线程,因此不适用于 async 编程。 37 | 38 | // 创建一个新通道,缓冲队列长度是 32。 39 | // 通道的缓冲队列长度是 32,意味着如果消息发送的比接收的快,这些消息将被存储在缓冲队列中, 40 | // 一旦存满了 32 条消息,使用send(...).await的发送者会进入睡眠, 41 | // 直到缓冲队列可以放入新的消息(被接收者消费了)。 42 | let (tx, mut rx) = mpsc::channel(32); 43 | 44 | // 你可以使用 clone 方法克隆多个发送者,但是接收者无法被克隆,因为我们的通道是 mpsc 类型。 45 | // Clone a `tx` handle for the second f 46 | let tx2 = tx.clone(); 47 | // 在我们的例子中,接收者在管理 redis 连接的任务中,当该任务发现所有发送者都关闭时, 48 | // 它知道它的使命可以完成了,因此它会关闭 redis 连接。 49 | 50 | // 创建一个管理任务,它会管理 redis 的连接,当然,首先需要创建一条到 redis 的连接。 51 | let manager = tokio::spawn(async move { 52 | // Open a connection to the mini-redis address. 53 | let mut client = client::connect("127.0.0.1:6379").await.unwrap(); 54 | 55 | while let Some(cmd) = rx.recv().await { 56 | match cmd { 57 | Command::Get { key, resp } => { 58 | let res = client.get(&key).await; 59 | // 往 oneshot 中发送消息时,并没有使用 .await, 60 | // 原因是该发送操作要么直接成功、要么失败,并不需要等待。 61 | // Ignore errors 62 | let _ = resp.send(res); 63 | } 64 | Command::Set { key, val, resp } => { 65 | let res = client.set(&key, val).await; 66 | // Ignore errors 67 | let _ = resp.send(res); 68 | } 69 | } 70 | } 71 | }); 72 | 73 | // Spawn two tasks, one setting a value and other querying for key that was 74 | // set. 75 | // 让两个任务发送命令到消息通道。 76 | // 当spawn被调用时,提供的future将立即在后台开始运行,即使你不等待返回的JoinHandle。 77 | let t1 = tokio::spawn(async move { 78 | let (resp_tx, resp_rx) = oneshot::channel(); 79 | let cmd = Command::Get { 80 | key: "foo".to_string(), 81 | resp: resp_tx, 82 | }; 83 | 84 | // Send the GET request 85 | if tx.send(cmd).await.is_err() { 86 | eprintln!("connection task shutdown"); 87 | return; 88 | } 89 | 90 | // Await the response 91 | let res = resp_rx.await; 92 | println!("GOT (Get) = {:?}", res); 93 | }); 94 | 95 | let t2 = tokio::spawn(async move { 96 | let (resp_tx, resp_rx) = oneshot::channel(); 97 | let cmd = Command::Set { 98 | key: "foo".to_string(), 99 | val: "bar".into(), 100 | resp: resp_tx, 101 | }; 102 | 103 | // Send the SET request 104 | if tx2.send(cmd).await.is_err() { 105 | eprintln!("connection task shutdown"); 106 | return; 107 | } 108 | 109 | // Await the response 110 | let res = resp_rx.await; 111 | println!("GOT (Set) = {:?}", res); 112 | }); 113 | 114 | // t1和t2的执行顺序是不确定的,因为它们是并发执行的。 115 | // 所以t1有可能GOT (Get) = Ok(Ok(None)),也有可能GOT (Get) = Ok(Ok(Some(b"bar"))) 116 | // tokio::spawn 返回一个 JoinHandle,它是一个 future,因此可以使用.await等待它的执行结果。 117 | t1.await.unwrap(); 118 | t2.await.unwrap(); 119 | manager.await.unwrap(); 120 | } 121 | -------------------------------------------------------------------------------- /实现一个简单的redis/my-redis-共享状态/src/bin/server.rs: -------------------------------------------------------------------------------- 1 | use bytes::Bytes; 2 | use mini_redis::{Connection, Frame}; 3 | use std::collections::HashMap; 4 | use std::sync::{Arc, Mutex}; 5 | use tokio::net::{TcpListener, TcpStream}; 6 | 7 | // cargo run --bin server 8 | #[tokio::main] 9 | async fn main() { 10 | // 上一章节中,咱们搭建了一个异步的 redis 服务器,并成功的提供了服务,但是其隐藏了一个 11 | // 巨大的问题:状态(数据)无法在多个连接之间共享,下面一起来看看该如何解决。 12 | // 好在 Tokio 十分强大,上面问题对应的解决方法也不止一种: 13 | // - 使用 Mutex 来保护数据的共享访问 14 | // - 生成一个异步任务去管理状态,然后各个连接使用消息传递的方式与其进行交互 15 | 16 | // 其中,第一种方法适合比较简单的数据,而第二种方法适用于需要异步工作的,例如 I/O 原语。 17 | // 由于我们使用的数据存储类型是 HashMap,使用到的 相关操作是 insert 和 get , 18 | // 又因为这两个操作都不是异步的,因此只要使用 Mutex 即可解决问题。 19 | 20 | let listener = TcpListener::bind("127.0.0.1:6379").await.unwrap(); 21 | println!("Listening"); 22 | 23 | let db = Arc::new(Mutex::new(HashMap::new())); 24 | // 我们使用了 std::sync::Mutex 来保护 HashMap,而不是使用 tokio::sync::Mutex。 25 | // 在使用 Tokio 编写异步代码时,一个常见的错误无条件地使用 tokio::sync::Mutex , 26 | // 而真相是:Tokio 提供的异步锁只应该在跨多个 .await调用时使用, 27 | // 而且 Tokio 的 Mutex 实际上内部使用的也是 std::sync::Mutex。 28 | 29 | loop { 30 | let (socket, _) = listener.accept().await.unwrap(); 31 | 32 | // 将 handle 克隆一份 33 | let db = db.clone(); 34 | tokio::spawn(async move { 35 | process(socket, db).await; 36 | }); 37 | } 38 | } 39 | 40 | // 类型别名,简化类型定义 41 | type Db = Arc>>; 42 | // 在上一节中,我们使用 Vec 来保存目标数据, 43 | // 但是它有一个问题,对它进行克隆时会将底层数据也整个复制一份,效率很低 44 | // Bytes 是一个引用计数类型,跟 Arc 非常类似,或者准确的说, 45 | // Bytes 就是基于 Arc 实现的,但相比后者Bytes 提供了一些额外的能力。 46 | async fn process(socket: TcpStream, db: Db) { 47 | use mini_redis::Command::{self, Get, Set}; 48 | 49 | let mut connection = Connection::new(socket); 50 | 51 | while let Some(frame) = connection.read_frame().await.unwrap() { 52 | let response = match Command::from_frame(frame).unwrap() { 53 | Set(cmd) => { 54 | let mut db = db.lock().unwrap(); 55 | db.insert(cmd.key().to_string(), cmd.value().clone()); 56 | Frame::Simple("OK".to_string()) 57 | } 58 | Get(cmd) => { 59 | let db = db.lock().unwrap(); 60 | if let Some(value) = db.get(cmd.key()) { 61 | Frame::Bulk(value.clone()) 62 | } else { 63 | Frame::Null 64 | } 65 | } 66 | cmd => panic!("unimplemented {:?}", cmd), 67 | }; 68 | 69 | connection.write_frame(&response).await.unwrap(); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /实现一个简单的redis/tokio_hello/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /实现一个简单的redis/tokio_hello/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tokio_hello" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | tokio = { version = "1.43", features = ["full"] } 10 | mini-redis = "0.4" 11 | 12 | [[example]] 13 | name = "example" 14 | path = "examples/*" 15 | -------------------------------------------------------------------------------- /实现一个简单的redis/tokio_hello/examples/main1.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{Arc, Mutex}; 2 | use tokio::task; 3 | 4 | /* 5 | https://users.rust-lang.org/t/how-to-handle-deterministic-but-not-static-lifetime-in-tokio/112089/3 6 | https://github.com/tokio-rs/tokio/issues/3162#issuecomment-2134405903 7 | 8 | 9 | tokio::spawn 要求闭包是 'static 的,这意味着闭包不能借用任何生命周期短于 'static 的数据。 10 | 这是因为 spawned 任务可能会在创建它的函数返回后继续执行,因此任何非 'static 的引用都可能变得无效。 11 | 12 | 然而,有几种方法可以解决这个问题,允许你在 Tokio 任务中使用非 'static 数据: 13 | 14 | 1. 使用 Arc 和 Mutex/RwLock (推荐) 15 | 16 | 这是最常见的也是最推荐的方法。将数据包装在 Arc> 或 Arc> 中,然后克隆 Arc 并将其移动到闭包中。 17 | 18 | 2. 在tokio外开一个新线程,使用thread::scope 19 | 20 | 3. 使用tokio::task::unconstrained,放弃异步阻塞当前线程直到任务完成 21 | 22 | 4. clone 23 | */ 24 | 25 | #[tokio::main] 26 | async fn main() { 27 | let data = vec![1, 2, 3]; 28 | let shared_data = Arc::new(Mutex::new(data)); 29 | 30 | let handle = task::spawn({ 31 | let shared_data = shared_data.clone(); // Clone the Arc 32 | async move { 33 | let mut data = shared_data.lock().unwrap(); 34 | data.push(4); 35 | println!("Data inside task: {:?}", data); 36 | } 37 | }); 38 | 39 | // ... Do other work ... 40 | 41 | handle.await.unwrap(); 42 | println!("Final data: {:?}", shared_data.lock().unwrap()); 43 | } 44 | -------------------------------------------------------------------------------- /实现一个简单的redis/tokio_hello/examples/main2.rs: -------------------------------------------------------------------------------- 1 | use tokio::time::sleep; 2 | 3 | #[tokio::main] 4 | async fn main() { 5 | let data = vec![1, 2, 3]; 6 | let data_ref = &data; // Borrow the data 7 | 8 | tokio::task::spawn(async move { 9 | let mut interval = tokio::time::interval(std::time::Duration::from_millis(200)); 10 | loop { 11 | interval.tick().await; 12 | println!("tick"); 13 | } 14 | }); 15 | 16 | tokio::task::unconstrained(async { 17 | sleep(std::time::Duration::from_secs(2)).await; 18 | // Access the borrowed data here 19 | println!("Data inside task: {:?}", data_ref); 20 | }) 21 | .await; 22 | 23 | println!("Final data: {:?}", data); // Still accessible here 24 | } 25 | -------------------------------------------------------------------------------- /实现一个简单的redis/tokio_hello/examples/main3.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | use tokio::task; 3 | 4 | // !Send (non-Send) futures in asynchronous Rust code. 5 | 6 | /* 7 | Tokio的Tokio::spawn函数用于并发地启动任务。它要求派生的future实现Send。这是一个安全保证:Tokio可能会把future安排在另一个线程上。 8 | 9 | 如果您尝试在tokio::spawn的future中使用!Send类型(如Rc),则会得到编译时错误。 10 | 11 | LocalSet保证派生到它的所有任务(未来)将在同一线程上运行。这消除了Send的需要,因为没有跨线程数据共享。 12 | 13 | Inside a LocalSet, you use task::spawn_local instead of tokio::spawn. 14 | */ 15 | 16 | #[tokio::main] 17 | async fn main() { 18 | let nonsend_data = Rc::new("my nonsend data..."); 19 | 20 | // Construct a local task set that can run `!Send` futures. 21 | let local = task::LocalSet::new(); 22 | 23 | // The run_until method can only be used in #[tokio::main], #[tokio::test] or directly inside a call to [Runtime::block_on]. 24 | // It cannot be used inside a task spawned with tokio::spawn. 25 | local 26 | .run_until(async move { 27 | let nonsend_data = nonsend_data.clone(); 28 | // `spawn_local` ensures that the future is spawned on the local 29 | // task set. 30 | task::spawn_local(async move { 31 | println!("{}", nonsend_data); 32 | // ... 33 | }) 34 | .await 35 | .unwrap(); 36 | }) 37 | .await; 38 | 39 | /* 40 | run_until 阻塞当前线程,直到给定的 future 完成。它主要用于运行一个作为"根"的 future, 41 | 这个 future 可能会生成其他任务(包括使用 spawn_local 生成的非 Send 任务)。 42 | 43 | 44 | spawn_local 在 LocalSet 上生成一个非 Send 的新任务。 它类似于 tokio::spawn,但用于非 Send 的 future。 45 | 46 | */ 47 | } 48 | -------------------------------------------------------------------------------- /实现一个简单的redis/tokio_hello/examples/main4.rs: -------------------------------------------------------------------------------- 1 | use tokio::runtime::Builder; 2 | use tokio::sync::{mpsc, oneshot}; 3 | use tokio::task::LocalSet; 4 | 5 | // Use inside tokio::spawn 6 | // `run_until method` adn `awaiting a LocalSet` , The two methods mentioned above cannot be used inside tokio::spawn, 7 | // so to spawn !Send futures from inside tokio::spawn, we need to do something else. 8 | // The solution is to create the LocalSet somewhere else, and communicate with it using an [mpsc] channel. 9 | // 即开一个新线程,使用单线程的tokio runtime接管,然后使用channel通信接收任务 10 | 11 | // The following example puts the LocalSet inside a new thread. 12 | 13 | // This struct describes the task you want to spawn. Here we include 14 | // some simple examples. The oneshot channel allows sending a response 15 | // to the spawner. 16 | #[derive(Debug)] 17 | #[allow(dead_code)] 18 | enum Task { 19 | PrintNumber(u32), 20 | AddOne(u32, oneshot::Sender), 21 | } 22 | 23 | #[derive(Clone)] 24 | struct LocalSpawner { 25 | send: mpsc::UnboundedSender, 26 | } 27 | 28 | impl LocalSpawner { 29 | pub fn new() -> Self { 30 | let (send, mut recv) = mpsc::unbounded_channel(); 31 | 32 | let rt = Builder::new_current_thread().enable_all().build().unwrap(); 33 | 34 | std::thread::spawn(move || { 35 | let local = LocalSet::new(); 36 | 37 | local.spawn_local(async move { 38 | while let Some(new_task) = recv.recv().await { 39 | tokio::task::spawn_local(run_task(new_task)); 40 | } 41 | // If the while loop returns, then all the LocalSpawner 42 | // objects have been dropped. 43 | }); 44 | 45 | // This will return once all senders are dropped and all 46 | // spawned tasks have returned. 47 | rt.block_on(local); 48 | }); 49 | 50 | Self { send } 51 | } 52 | 53 | pub fn spawn(&self, task: Task) { 54 | self.send 55 | .send(task) 56 | .expect("Thread with LocalSet has shut down."); 57 | } 58 | } 59 | 60 | // This task may do !Send stuff. We use printing a number as an example, 61 | // but it could be anything. 62 | // 63 | // The Task struct is an enum to support spawning many different kinds 64 | // of operations. 65 | async fn run_task(task: Task) { 66 | match task { 67 | Task::PrintNumber(n) => { 68 | println!("{}", n); 69 | } 70 | Task::AddOne(n, response) => { 71 | // We ignore failures to send the response. 72 | let _ = response.send(n + 1); 73 | } 74 | } 75 | } 76 | 77 | #[tokio::main] 78 | async fn main() { 79 | let spawner = LocalSpawner::new(); 80 | 81 | let (send, response) = oneshot::channel(); 82 | spawner.spawn(Task::AddOne(10, send)); 83 | let eleven = response.await.unwrap(); 84 | assert_eq!(eleven, 11); 85 | } 86 | -------------------------------------------------------------------------------- /实现一个简单的redis/tokio_hello/examples/main5.rs: -------------------------------------------------------------------------------- 1 | use std::future::Future; 2 | use std::mem::take; 3 | use std::pin::Pin; 4 | use std::task::{Context, Poll}; 5 | 6 | use tokio::sync::mpsc; 7 | use tokio::time::{Duration, sleep}; 8 | 9 | #[tokio::main(flavor = "current_thread")] 10 | async fn main() { 11 | tokio::spawn(task1()); 12 | // tokio::spawn(task2()).await.unwrap(); 13 | tokio::spawn(tokio::task::unconstrained(task2())) 14 | .await 15 | .unwrap(); 16 | // let _fut = tokio::task::unconstrained(task2()); 17 | // task1().await; 18 | // fut.await; 19 | } 20 | 21 | async fn task1() { 22 | loop { 23 | println!("task1"); 24 | tokio::time::sleep(std::time::Duration::from_millis(100)).await; 25 | } 26 | } 27 | 28 | async fn task2() { 29 | let (tx, mut rx) = mpsc::unbounded_channel(); 30 | loop { 31 | let _ = tx.send(()); 32 | // This will always be ready. If coop was in effect, this code would be forced to yield 33 | // periodically. However, if left unconstrained, then this code will never yield. 34 | rx.recv().await; 35 | 36 | // let poll_always_ok = PollAlwaysOk::new(); 37 | // poll_always_ok.await; 38 | // tokio::task::yield_now().await; // 手动yield 插入 yield point 39 | 40 | // println!("task2"); 41 | // tokio::time::sleep(std::time::Duration::from_millis(1)).await; 42 | } 43 | } 44 | 45 | struct PollAlwaysOk(bool); 46 | 47 | impl PollAlwaysOk { 48 | fn new() -> Self { 49 | Self(true) 50 | } 51 | } 52 | 53 | impl Future for PollAlwaysOk { 54 | type Output = (); 55 | 56 | fn poll(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll { 57 | if self.0 { 58 | self.0 = false; 59 | Poll::Pending 60 | } else { 61 | Poll::Ready(()) 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /实现一个简单的redis/tokio_hello/examples/main6.rs: -------------------------------------------------------------------------------- 1 | use std::time::Instant; 2 | use tokio::task; 3 | use tokio::time::{Duration, sleep}; 4 | #[tokio::main(flavor = "current_thread")] 5 | async fn main() { 6 | let now = Instant::now(); 7 | 8 | let regular_task = async move { 9 | println!("Regular task: Started,{}", now.elapsed().as_millis()); 10 | sleep(Duration::from_millis(500)).await; 11 | println!("Regular task: Resumed,{}", now.elapsed().as_millis()); 12 | }; 13 | 14 | tokio::spawn(regular_task); 15 | 16 | let unconstrained_task = task::unconstrained(async { 17 | println!( 18 | "Unconstrained task: Before sleep,{}", 19 | now.elapsed().as_millis() 20 | ); 21 | // ??? 22 | sleep(Duration::from_secs(1)).await; 23 | println!( 24 | "Unconstrained task: After sleep,{}", 25 | now.elapsed().as_millis() 26 | ); 27 | 28 | println!("Unconstrained task: Before yield_now"); 29 | task::yield_now().await; 30 | println!("Unconstrained task: After yield_now"); 31 | }); 32 | 33 | unconstrained_task.await; 34 | println!("Main task complete"); 35 | } 36 | -------------------------------------------------------------------------------- /实现一个简单的redis/tokio_select/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /实现一个简单的redis/tokio_select/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tokio_select" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | tokio = { version = "1", features = ["full"] } 10 | -------------------------------------------------------------------------------- /实现一个简单的redis/tokio_stream/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /实现一个简单的redis/tokio_stream/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tokio_stream" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | tokio = { version = "1", features = ["full"] } 10 | tokio-stream = "0.1" 11 | async-stream = "0.3" 12 | futures-util = "0.3" 13 | futures-core = "0.3" 14 | -------------------------------------------------------------------------------- /实现一个简单的redis/tokio_stream/src/main.rs: -------------------------------------------------------------------------------- 1 | use async_stream::stream; 2 | 3 | use futures_util::pin_mut; 4 | use futures_util::stream::StreamExt; 5 | 6 | // 在实际场景中,迭代一个集合,然后异步的去执行是很常见的需求, 7 | // 好在 Tokio 为我们提供了 stream,我们可以在异步函数中对其进行迭代, 8 | // 甚至和迭代器 Iterator 一样,stream 还能使用适配器,例如 map ! 9 | // Tokio 在 StreamExt 特征上定义了常用的适配器。 10 | 11 | // stream 没有放在 `tokio` 包的原因在于标准库中的 `Stream` 特征还没有稳定, 12 | // 一旦稳定后,`stream` 将移动到 `tokio` 中来 13 | #[tokio::main] 14 | async fn main() { 15 | // tokio_stream 16 | study_tokio_stream().await; 17 | 18 | // async_stream 19 | study_async_stream().await; 20 | } 21 | 22 | async fn study_tokio_stream() { 23 | println!("------------------ study_tokio_stream ------------------"); 24 | println!("------------------ example1 ------------------"); 25 | async fn example1() { 26 | let mut stream = tokio_stream::iter(&[1, 2, 3]); 27 | 28 | // 和迭代器 Iterator 类似,next() 方法返回一个 Option, 29 | // 其中 T 是从 stream 中获取的值的类型。若收到 None 则意味着 stream 迭代已经结束。 30 | while let Some(v) = stream.next().await { 31 | println!("GOT = {:?}", v); 32 | } 33 | } 34 | example1().await; 35 | } 36 | 37 | async fn study_async_stream() { 38 | println!("------------------ study_async_stream ------------------"); 39 | 40 | println!("------------------ example1 ------------------"); 41 | async fn example1() { 42 | let s = stream! { 43 | for i in 0..3 { 44 | yield i; 45 | } 46 | }; 47 | pin_mut!(s); // needed for iteration 48 | 49 | while let Some(value) = s.next().await { 50 | println!("got {}", value); 51 | } 52 | } 53 | example1().await; 54 | 55 | println!("------------------ example2 ------------------"); 56 | async fn example2() { 57 | // returned by using :impl Stream 58 | use futures_core::stream::Stream; 59 | fn zero_to_three() -> impl Stream { 60 | stream! { 61 | for i in 0..3 { 62 | yield i; 63 | } 64 | } 65 | } 66 | 67 | let s = zero_to_three(); 68 | pin_mut!(s); // needed for iteration 69 | 70 | while let Some(value) = s.next().await { 71 | println!("got {}", value); 72 | } 73 | } 74 | example2().await; 75 | 76 | println!("------------------ example3 ------------------"); 77 | // Streams may be implemented in terms of other streams - provides syntax 78 | // to assist with this:async-stream `for await` 79 | async fn example3() { 80 | use futures_core::stream::Stream; 81 | fn zero_to_three() -> impl Stream { 82 | stream! { 83 | for i in 0..3 { 84 | yield i; 85 | } 86 | } 87 | } 88 | fn double>(input: S) -> impl Stream { 89 | stream! { 90 | for await value in input { 91 | yield value * 2; 92 | } 93 | } 94 | } 95 | let s = double(zero_to_three()); 96 | pin_mut!(s); // needed for iteration 97 | 98 | while let Some(value) = s.next().await { 99 | println!("got {}", value); 100 | } 101 | } 102 | example3().await; 103 | 104 | println!("------------------ example4 ------------------"); 105 | async fn _example4() { 106 | use async_stream::try_stream; 107 | use futures_core::stream::Stream; 108 | use std::io; 109 | use std::net::SocketAddr; 110 | use tokio::net::{TcpListener, TcpStream}; 111 | 112 | fn bind_and_accept(addr: SocketAddr) -> impl Stream> { 113 | try_stream! { 114 | let listener = TcpListener::bind(addr).await?; 115 | 116 | loop { 117 | let (stream, addr) = listener.accept().await?; 118 | println!("received on {:?}", addr); 119 | yield stream; 120 | } 121 | } 122 | } 123 | use std::net::{IpAddr, Ipv4Addr}; 124 | let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 7777); 125 | let s = bind_and_accept(socket); 126 | pin_mut!(s); // needed for iteration 127 | 128 | while let Some(value) = s.next().await { 129 | println!("got {:?}", value); 130 | if let Ok(stream) = value { 131 | println!("stream: {:?}", stream); 132 | } 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /实现一个简单的redis/tokio_tcp/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /实现一个简单的redis/tokio_tcp/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tcp" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | tokio = { version = "1", features = ["full"] } 10 | -------------------------------------------------------------------------------- /实现一个简单的redis/tokio_tcp/src/bin/client.rs: -------------------------------------------------------------------------------- 1 | #[tokio::main] 2 | async fn main() { 3 | let stream = tokio::net::TcpStream::connect("127.0.0.1:8081") 4 | .await 5 | .unwrap(); 6 | 7 | /* 8 | loop { 9 | let mut input = String::new(); 10 | println!("please input msg: "); 11 | std::io::stdin().read_line(&mut input).unwrap(); 12 | stream.write_all(input.as_bytes()).await.unwrap(); 13 | let n = stream.read(&mut buf).await.unwrap(); 14 | let msg = String::from_utf8_lossy(&buf[..n]); 15 | println!("recv: {}", msg.trim()); 16 | } 17 | */ 18 | // 上面的代码必须先输入,才能接收到服务端的消息。 19 | // 想要实现并发读写,显然,使用同一个 socket 是不行的, 20 | // 为了实现目标功能,必须将 `socket` 分离成一个读取器和写入器。 21 | 22 | // let (reader, mut writer) = tokio::io::split(stream); 23 | 24 | // 实际上,任何一个读写器( reader + writer )都可以使用 io::split 方法进行分离,最终返回一个读取器和写入器, 25 | // 这两者可以独自的使用,例如可以放入不同的任务中。 26 | // io::split 可以用于任何同时实现了 AsyncRead 和 AsyncWrite 的值, 27 | // 它的内部使用了 Arc 和 Mutex 来实现相应的功能。 28 | 29 | // 如果大家觉得这种实现有些重,可以使用 Tokio 提供的 `TcpStream`,它提供了两种方式进行分离: 30 | // - TcpStream::split 会获取字节流的引用,然后将其分离成一个读取器和写入器。 31 | // 但由于使用了引用的方式,它们俩必须和 `split` 在同一个任务中。 32 | // 优点就是,这种实现没有性能开销,因为无需 `Arc` 和 `Mutex`。 33 | // - TcpStream::into_split 还提供了一种分离实现,分离出来的结果可以在任务间移动,内部是通过 `Arc` 实现。 34 | 35 | // TcpStream::split 36 | let (mut reader, mut writer) = stream.into_split(); 37 | 38 | tokio::spawn(async move { 39 | loop { 40 | use tokio::io::AsyncReadExt; 41 | let mut buf = vec![0; 1024]; 42 | let n = reader.read(&mut buf).await; 43 | if n.is_err() { 44 | println!("read error: {:?}", n.err()); 45 | return; 46 | } 47 | let n = n.unwrap(); 48 | let msg = String::from_utf8_lossy(&buf[..n]); 49 | println!("recv: {}", msg.trim()); 50 | } 51 | }); 52 | 53 | loop { 54 | use tokio::io::AsyncWriteExt; 55 | let mut input = String::new(); 56 | println!("please input msg: "); 57 | std::io::stdin().read_line(&mut input).unwrap(); 58 | writer.write_all(input.as_bytes()).await.unwrap(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /实现一个简单的redis/tokio_tcp/src/bin/server.rs: -------------------------------------------------------------------------------- 1 | // 举一个tokio官方的代码例子,这段代码实现了一个tcp server,会将客户端发来的数据原封不动的发回去: 2 | use tokio::io::{AsyncReadExt, AsyncWriteExt}; 3 | use tokio::net::{TcpListener, TcpStream}; 4 | 5 | use std::error::Error; 6 | 7 | // cargo run --bin server 8 | #[tokio::main] 9 | async fn main() -> Result<(), Box> { 10 | let listener = TcpListener::bind("127.0.0.1:8081").await?; 11 | 12 | loop { 13 | let (socket, socket_addr) = listener.accept().await?; 14 | println!("accept socket: {:?}", socket_addr); 15 | tokio::spawn(handler(socket)); 16 | } 17 | } 18 | 19 | async fn handler(mut socket: TcpStream) { 20 | let mut buf = vec![0; 1024]; 21 | 22 | loop { 23 | let n = socket 24 | .read(&mut buf) 25 | .await 26 | .expect("failed to read data from socket"); 27 | 28 | if n == 0 { 29 | return; 30 | } 31 | println!("recv: {}", String::from_utf8_lossy(&buf[..n]).trim()); 32 | socket 33 | .write_all(&buf[0..n]) 34 | .await 35 | .expect("failed to write data to socket"); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /实现一个简单的redis/tokio_优雅的关闭/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /实现一个简单的redis/tokio_优雅的关闭/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tokio_优雅的关闭" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | tokio = { version = "1", features = ["full"] } 10 | -------------------------------------------------------------------------------- /实现一个简单的redis/tokio_优雅的关闭/src/main.rs: -------------------------------------------------------------------------------- 1 | // 如果你的服务是一个小说阅读网站,那大概率用不到优雅关闭的, 2 | // 简单粗暴的关闭服务器,然后用户再次请求时获取一个错误就是了。 3 | // 但如果是一个 web 服务或数据库服务呢?当前的连接很可能在做着重要的事情, 4 | // 一旦关闭会导致数据的丢失甚至错误,此时,我们就需要优雅的关闭(graceful shutdown)了。 5 | 6 | #[tokio::main] 7 | async fn main() { 8 | // 一般来说,何时关闭是取决于应用自身的,但是一个常用的关闭准则就是当应用收到来自于操作系统的关闭信号时。 9 | // 例如通过 ctrl + c 来关闭正在运行的命令行程序。 10 | 11 | // 为了检测来自操作系统的关闭信号,Tokio 提供了一个 tokio::signal::ctrl_c 函数, 12 | // 它将一直睡眠直到收到对应的信号: 13 | // ... spawn application as separate task ... 14 | // 在一个单独的任务中处理应用逻辑 15 | 16 | tokio::spawn(async { 17 | match tokio::signal::ctrl_c().await { 18 | Ok(()) => { 19 | println!("ctrl-c received!") 20 | } 21 | Err(err) => { 22 | eprintln!("Unable to listen for shutdown signal: {}", err); 23 | } 24 | } 25 | }); 26 | 27 | // 通知程序的每一个部分开始关闭 28 | // 发送关闭信号给应用所在的任务,然后等待 29 | 30 | // 最常见的通知程序各个部分关闭的方式就是使用一个广播消息通道。 31 | // 关于如何实现,其实也不复杂:应用中的每个任务都持有一个广播消息通道的接收端, 32 | // 当消息被广播到该通道时,每个任务都可以收到该消息,并关闭自己: 33 | 34 | // 我们讲到过一个 mpsc 消息通道有一个重要特性:当所有发送端都 drop 时,消息通道会自动关闭, 35 | // 此时继续接收消息就会报错。 36 | // 大家发现没?这个特性特别适合优雅关闭的场景:主线程持有消息通道的接收端, 37 | // 然后每个代码部分拿走一个发送端,当该部分结束时,就 drop 掉发送端, 38 | // 因此所有发送端被 drop 也就意味着所有的部分都已关闭,此时主线程的接收端就会收到错误,进而结束。 39 | 40 | use tokio::sync::mpsc::{Sender, channel}; 41 | use tokio::time::{Duration, sleep}; 42 | 43 | async fn some_operation(i: u64, _sender: Sender<()>) { 44 | sleep(Duration::from_millis(100 * i)).await; 45 | println!("Task {} shutting down.", i); 46 | // 发送端超出作用域,然后被 drop 47 | } 48 | 49 | let (send, mut recv) = channel(1); 50 | 51 | for i in 0..10 { 52 | tokio::spawn(some_operation(i, send.clone())); 53 | } 54 | 55 | // 等待各个任务的完成 56 | // 57 | // 我们需要 drop 自己的发送端,因为等下的 recv() 调用会阻塞, 如果不 drop ,那发送端就无法被全部关闭 58 | // recv 也将永远无法结束,这将陷入一个类似死锁的困境 59 | drop(send); 60 | 61 | // 当所有发送端都超出作用域被 drop 时 (当前的发送端并不是因为超出作用域被 drop 而是手动 drop 的) 62 | // recv 调用会返回一个错误 63 | let _ = recv.recv().await; 64 | } 65 | -------------------------------------------------------------------------------- /实现一个简单的redis/tokio_协作式调度/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /实现一个简单的redis/tokio_协作式调度/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tokio_协作式调度" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | tokio = { version = "1.16.1", features = ["full"] } 10 | rand = "0.8.4" 11 | 12 | 13 | [[example]] 14 | name = "example1" 15 | path = "examples/example1.rs" 16 | -------------------------------------------------------------------------------- /实现一个简单的redis/tokio_协作式调度/examples/example1.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let rt = tokio::runtime::Builder::new_multi_thread() 3 | .worker_threads(1) 4 | .enable_all() 5 | .build() 6 | .unwrap(); 7 | 8 | let j1 = rt.spawn(async { 9 | other_worker().await; 10 | }); 11 | 12 | println!("main: other_worker spawned"); 13 | 14 | rt.spawn(async { 15 | http_task().await; 16 | }); 17 | println!("main: http_task spawned"); 18 | 19 | rt.block_on(j1).unwrap(); 20 | } 21 | 22 | async fn other_worker() { 23 | let mut counter = 0; 24 | loop { 25 | counter += 1; 26 | println!("otherWorker: {}", counter); 27 | tokio::time::sleep(tokio::time::Duration::from_millis(500)).await; 28 | } 29 | } 30 | 31 | async fn http_task() { 32 | let mut counter = 0; 33 | while counter < 1000000000 { 34 | counter += 1; 35 | if counter % 50000000 == 0 { 36 | println!("httpTask: {}", counter); 37 | } 38 | } 39 | println!("httpTask: try to get http://www.baidu.com"); 40 | tokio::time::sleep(tokio::time::Duration::from_secs(3)).await; 41 | println!("httpTask: get success,begin to parse html"); 42 | tokio::time::sleep(tokio::time::Duration::from_secs(3)).await; 43 | println!("httpTask: done"); 44 | } 45 | -------------------------------------------------------------------------------- /实现一个简单的redis/tokio_协作式调度/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::{io, thread, time::Duration}; 2 | 3 | use tokio::runtime::Builder; 4 | 5 | use rand::Rng; 6 | use tokio::time::Instant; 7 | 8 | async fn worker(i: i32) { 9 | println!("current thread: {}", thread::current().name().unwrap()); 10 | let mut count = 0; 11 | loop { 12 | let start = Instant::now(); 13 | let mut num: i64 = 0; 14 | loop { 15 | num += rand::thread_rng().gen_range(1..=100); 16 | let elapsed = start.elapsed(); 17 | if elapsed.as_millis() > 200 { 18 | break; 19 | } 20 | } 21 | println!("worker {}: {}", i, num); 22 | count += 1; 23 | if count > 20 { 24 | println!("worker {} exit", i); 25 | break; 26 | } 27 | } 28 | } 29 | 30 | fn main() -> io::Result<()> { 31 | // 默认情况下,Tokio 启动的工作线程数和 CPU 核数相等,也可以自定义。 32 | let runtime = Builder::new_multi_thread() 33 | .enable_all() 34 | .worker_threads(1) 35 | .build()?; 36 | 37 | runtime.spawn(worker(1)); 38 | // tokio 是协作式调度,所以如果想要让 worker 2 有机会执行,就需要让 worker 1 主动让出 CPU。 39 | runtime.spawn(worker(2)); 40 | 41 | println!("current thread: {}", thread::current().name().unwrap()); 42 | let mut count = 0; 43 | loop { 44 | let start = Instant::now(); 45 | let mut num: i64 = 0; 46 | loop { 47 | num += rand::thread_rng().gen_range(1..=100); 48 | let elapsed = start.elapsed(); 49 | if elapsed.as_millis() > 200 { 50 | break; 51 | } 52 | } 53 | println!("worker main: {}", num); 54 | count += 1; 55 | if count > 20 { 56 | println!("worker main exit"); 57 | break; 58 | } 59 | } 60 | 61 | println!("current thread: {}", thread::current().name().unwrap()); 62 | // thread::sleep(Duration::from_secs(4444)); 63 | // runtime.shutdown_timeout(Duration::from_secs(4)); 64 | 65 | println!("exit main"); 66 | Ok(()) 67 | } 68 | -------------------------------------------------------------------------------- /实现一个简单的redis/tokio_异步原理/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /实现一个简单的redis/tokio_异步原理/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tokio_异步原理" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | tokio = { version = "1", features = ["full"] } 10 | -------------------------------------------------------------------------------- /实现一个简单的redis/tokio_异步原理/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::future::Future; 2 | use std::pin::Pin; 3 | use std::task::{Context, Poll}; 4 | use std::time::{Duration, Instant}; 5 | 6 | // 下面来一起实现个五脏俱全的 `Future`,它将: 7 | // 1. 等待某个特定时间点的到来 2. 在标准输出打印文本 3. 生成一个字符串 8 | struct Delay { 9 | when: Instant, 10 | } 11 | 12 | // 为我们的 Delay 类型实现 Future 特征 13 | impl Future for Delay { 14 | type Output = &'static str; 15 | 16 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<&'static str> { 17 | if Instant::now() >= self.when { 18 | // 时间到了,Future 可以结束 19 | println!("Hello world"); 20 | // Future 执行结束并返回 "done" 字符串 21 | Poll::Ready("done") 22 | } else { 23 | // 目前先忽略下面这行代码 24 | cx.waker().wake_by_ref(); 25 | Poll::Pending 26 | } 27 | } 28 | } 29 | 30 | #[tokio::main] 31 | async fn main() { 32 | let when = Instant::now() + Duration::from_millis(10); 33 | let future = Delay { when }; 34 | 35 | // 运行并等待 Future 的完成 36 | let out = future.await; 37 | 38 | // 判断 Future 返回的字符串是否是 "done" 39 | assert_eq!(out, "done"); 40 | } 41 | -------------------------------------------------------------------------------- /实现一个简单的redis/tokio_异步跟同步共存/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /实现一个简单的redis/tokio_异步跟同步共存/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "异步跟同步共存" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | tokio = { version = "1", features = ["full"] } 10 | bytes = "1" 11 | -------------------------------------------------------------------------------- /实现一个简单的redis/tokio_异步跟同步共存/src/main.rs: -------------------------------------------------------------------------------- 1 | /* 2 | 在 Rust 中, `main` 函数不能是异步的,有同学肯定不愿意了,我们在之前章节..不对,就在开头, 3 | 你还用到了 `async fn main` 的声明方式,怎么就不能异步了呢? 4 | 5 | #[tokio::main] 6 | async fn main() { 7 | println!("Hello world"); 8 | } 9 | 10 | 其实,`#[tokio::main]` 该宏仅仅是提供语法糖,目的是让大家可以更简单、更一致的去写异步代码, 11 | 它会将你写下的`async fn main` 函数替换为: 12 | 13 | fn main() { 14 | tokio::runtime::Builder::new_multi_thread() 15 | .enable_all() 16 | .build() 17 | .unwrap() 18 | .block_on(async { 19 | println!("Hello world"); 20 | }) 21 | } 22 | 23 | 注意到上面的 `block_on` 方法了嘛?在我们自己的同步代码中,可以使用它开启一个 `async/await` 世界。 24 | 25 | */ 26 | 27 | /* 28 | You should not use `block_on` frequently within your asynchronous code itself. 29 | Use `tokio::spawn` to create new asynchronous tasks. 30 | 31 | Nested block_on: 32 | 不能在另一个异步函数或另一个block_on调用中调用block_on。这将导致deadlock (block_on将尝试阻止外部异步上下文所依赖的线程)。Tokio检测到这一点和恐慌。 33 | 34 | 避免嵌套 block_on: 无论使用哪种运行时,都 绝对不要 在一个 async 函数内部或另一个 block_on 调用内部调用 block_on。这会导致死锁。 35 | block_on 用于顶层: block_on 主要用于程序的顶层(例如,在 main 函数中)来启动异步任务。在异步代码中,应该使用 tokio::spawn 来创建新的任务,而不是 block_on。 36 | */ 37 | 38 | // 本章节的目标很纯粹:如何在同步代码中使用一小部分异步代码。 39 | // 在一些场景中,你可能只想在异步程序中运行一小部分同步代码,这种需求可以考虑下 spawn_blocking。 40 | // 但是在很多场景中,我们只想让程序的某一个部分成为异步的,也许是因为同步代码更好实现, 41 | // 又或许是同步代码可读性、兼容性都更好。例如一个 GUI 应用可能想要让 UI 相关的代码在主线程中, 42 | // 然后通过另一个线程使用 tokio 的运行时来处理一些异步任务。 43 | mod other; 44 | 45 | use tokio::runtime::Builder; 46 | use tokio::time::{Duration, sleep}; 47 | 48 | fn main() { 49 | let runtime = Builder::new_multi_thread() 50 | .worker_threads(1) 51 | .enable_all() 52 | .build() 53 | .unwrap(); 54 | 55 | let mut handles = Vec::with_capacity(10); 56 | for i in 0..10 { 57 | handles.push(runtime.spawn(my_bg_task(i))); 58 | } 59 | 60 | // 在后台任务运行的同时做一些耗费时间的事情 61 | std::thread::sleep(Duration::from_millis(750)); 62 | println!("Finished time-consuming task."); 63 | 64 | // 等待这些后台任务的完成 65 | for handle in handles { 66 | // `spawn` 方法返回一个 `JoinHandle`,它是一个 `Future`,因此可以通过 `block_on` 来等待它完成 67 | runtime.block_on(handle).unwrap(); 68 | } 69 | // 在此例中,我们生成了 10 个后台任务在运行时中运行,然后等待它们的完成。 70 | // 作为一个例子,想象一下在图形渲染应用( GUI )中,有时候需要通过网络访问远程服务来获取一些数据, 71 | // 那上面的这种模式就非常适合,因为这些网络访问比较耗时,而且不会影响图形的主体渲染, 72 | // 因此可以在主线程中渲染图形,然后使用其它线程来运行 Tokio 的运行时, 73 | // 并通过该运行时使用异步的方式完成网络访问,最后将这些网络访问的结果发送到 GUI 进行数据渲染,例如一个进度条。 74 | 75 | // 还有一点很重要,在本例子中只能使用 `multi_thread` 运行时。 76 | // 如果我们使用了 `current_thread`,你会发现主线程的耗时任务会在后台任务开始之前就完成了。 77 | // 因为在 `current_thread` 模式下,生成的任务只会在 `block_on` 期间才执行。 78 | 79 | // 在 `multi_thread` 模式下,我们并不需要通过 `block_on` 来触发任务的运行, 80 | // 这里仅仅是用来阻塞并等待最终的结果。而除了通过 `block_on` 等待结果外,你还可以: 81 | // - 使用消息传递的方式,例如 `tokio::sync::mpsc`, 82 | // 让异步任务将结果发送到主线程,然后主线程通过 `.recv`方法等待这些结果 83 | // - 通过共享变量的方式,例如 `Mutex`,这种方式非常适合实现 GUI 的进度条: GUI 在每个渲染帧读取该变量即可。 84 | 85 | println!("--------------------------生成一个运行时--------------------------"); 86 | // 在同步代码中使用异步的另一个方法就是生成一个运行时,然后使用消息传递的方式跟它进行交互。 87 | // 这个方法虽然更啰嗦一些,但是相对于之前的两种方法更加灵活: 88 | 89 | let t: other::Task = other::Task { 90 | name: "foo task".to_string(), 91 | }; 92 | let scheduler = other::TaskSpawner::new(); 93 | scheduler.spawn_task(t); 94 | } 95 | 96 | async fn my_bg_task(i: u64) { 97 | let millis = 1000 - 50 * i; 98 | println!("Task {} sleeping for {} ms.", i, millis); 99 | 100 | sleep(Duration::from_millis(millis)).await; 101 | 102 | println!("Task {} stopping.", i); 103 | } 104 | -------------------------------------------------------------------------------- /实现一个简单的redis/tokio_异步跟同步共存/src/other.rs: -------------------------------------------------------------------------------- 1 | use tokio::runtime::Builder; 2 | use tokio::sync::mpsc; 3 | 4 | pub struct Task { 5 | pub name: String, 6 | // 一些信息用于描述该任务 7 | } 8 | 9 | async fn handle_task(task: Task) { 10 | println!("Got task {}", task.name); 11 | } 12 | 13 | #[derive(Clone)] 14 | pub struct TaskSpawner { 15 | spawn: mpsc::Sender, 16 | } 17 | 18 | impl TaskSpawner { 19 | pub fn new() -> TaskSpawner { 20 | // 创建一个消息通道用于通信 21 | let (send, mut recv) = mpsc::channel(16); 22 | 23 | let rt = Builder::new_current_thread().enable_all().build().unwrap(); 24 | 25 | std::thread::spawn(move || { 26 | rt.block_on(async move { 27 | while let Some(task) = recv.recv().await { 28 | tokio::spawn(handle_task(task)); 29 | } 30 | 31 | // 一旦所有的发送端超出作用域被 drop 后,`.recv()` 方法会返回 None,同时 while 循环会退出,然后线程结束 32 | }); 33 | }); 34 | 35 | TaskSpawner { spawn: send } 36 | } 37 | 38 | pub fn spawn_task(&self, task: Task) { 39 | match self.spawn.blocking_send(task) { 40 | Ok(()) => println!("spawn_task Ok"), 41 | Err(_) => panic!("The shared runtime has shut down."), 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /并发编程/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /并发编程/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "并发编程" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | crossbeam-channel = "0.5.8" 10 | crossbeam-utils = "0.8.15" 11 | thread_local = "1.1.7" 12 | tokio = { version = "1", features = ["full"] } 13 | lazy_static = "1.4" 14 | 15 | [[example]] 16 | name = "example1" 17 | path = "examples/*" 18 | -------------------------------------------------------------------------------- /并发编程/examples/alternate.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func worker(notifyCh chan int, id int) { 6 | for i := range notifyCh { 7 | fmt.Printf("worker %d: %d\n", id, i) 8 | notifyCh <- i + 1 9 | } 10 | } 11 | 12 | // 两个线程/协程交替打印数字 13 | func main() { 14 | notifyCh := make(chan int) 15 | go worker(notifyCh, 1) 16 | notifyCh <- 0 17 | worker(notifyCh, 2) 18 | } 19 | -------------------------------------------------------------------------------- /并发编程/examples/alternate1.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{Mutex, mpsc}; 2 | 3 | fn worker(sender: mpsc::SyncSender, receiver: &Mutex>, id: i32) { 4 | loop { 5 | let i = receiver.lock().unwrap().recv().unwrap(); 6 | println!("worker {}: {}", id, i); 7 | sender.send(i + 1).unwrap(); 8 | } 9 | } 10 | 11 | // 两个线程/协程交替打印数字 12 | fn main() { 13 | let (tx, rx) = mpsc::sync_channel(0); 14 | let rx = Mutex::new(rx); 15 | std::thread::scope(|s| { 16 | s.spawn(|| worker(tx.clone(), &rx, 1)); 17 | tx.send(0).unwrap(); 18 | worker(tx.clone(), &rx, 2); 19 | }); 20 | } 21 | -------------------------------------------------------------------------------- /并发编程/examples/alternate2.rs: -------------------------------------------------------------------------------- 1 | use std::sync::mpsc; 2 | 3 | fn worker(sender: mpsc::SyncSender, receiver: UnsafeRef>, id: i32) { 4 | loop { 5 | let i = receiver.recv().unwrap(); 6 | println!("worker {}: {}", id, i); 7 | sender.send(i + 1).unwrap(); 8 | } 9 | } 10 | 11 | // unsafe实现,去除receiver加的锁 12 | fn main() { 13 | let (tx, rx) = mpsc::sync_channel(0); 14 | let rx = UnsafeRef::new(&rx); 15 | std::thread::scope(|s| { 16 | { 17 | let tx = tx.clone(); 18 | let rx = rx.clone(); 19 | s.spawn(|| worker(tx, rx, 1)); 20 | } 21 | tx.send(0).unwrap(); 22 | worker(tx, rx, 2); 23 | }); 24 | } 25 | 26 | struct UnsafeRef { 27 | inner: *const T, 28 | } 29 | 30 | impl UnsafeRef { 31 | fn new(inner: *const T) -> Self { 32 | Self { inner } 33 | } 34 | } 35 | 36 | impl Clone for UnsafeRef { 37 | fn clone(&self) -> Self { 38 | Self { inner: self.inner } 39 | } 40 | } 41 | impl std::ops::Deref for UnsafeRef { 42 | type Target = T; 43 | fn deref(&self) -> &Self::Target { 44 | unsafe { &*self.inner } 45 | } 46 | } 47 | 48 | unsafe impl std::marker::Sync for UnsafeRef {} 49 | unsafe impl std::marker::Send for UnsafeRef {} 50 | -------------------------------------------------------------------------------- /并发编程/examples/alternate3.rs: -------------------------------------------------------------------------------- 1 | use tokio::sync::oneshot; 2 | 3 | #[derive(Debug)] 4 | struct DataSender { 5 | data: T, 6 | sender: Box>>, 7 | } 8 | 9 | async fn worker(mut receiver: oneshot::Receiver>, id: i32) { 10 | loop { 11 | let s = receiver.await.unwrap(); 12 | println!("worker {}: {}", id, s.data); 13 | let (tx, rx) = oneshot::channel(); 14 | s.sender 15 | .send(DataSender { 16 | data: s.data + 1, 17 | sender: Box::new(tx), 18 | }) 19 | .unwrap(); 20 | receiver = rx; 21 | } 22 | } 23 | 24 | // 两个线程/协程交替打印数字 25 | // Rust异步实现 26 | #[tokio::main] 27 | async fn main() { 28 | let (tx, rx) = oneshot::channel(); 29 | let (tx2, rx2) = oneshot::channel(); 30 | tokio::spawn(worker(rx, 1)); 31 | tx.send(DataSender { 32 | data: 0, 33 | sender: Box::new(tx2), 34 | }) 35 | .unwrap(); 36 | worker(rx2, 2).await 37 | } 38 | -------------------------------------------------------------------------------- /并发编程/examples/barrier.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{Arc, Barrier, Mutex}; 2 | 3 | // Barrier 可以用 wait 来控制 n 个线程的同步,数量需要提前指明。 4 | // 当调用 wait 时,如果不是 n 个都完成,就会一直阻塞当前线程,直到第 n 个 wait 调用,才能进行后续操作。 5 | // 这种机制就像在多个线程中插入了一道屏障,当所有线程都执行到这里时,才能解除屏障继续向后执行。 6 | // 这样实现在线程数量大的时候是会有比较明显的性能开销的,底层是使用 condvar+mutex 来实现的。 7 | 8 | fn main() { 9 | let numthreads = 10; 10 | let my_mutex = Arc::new(Mutex::new(0)); 11 | 12 | // We use a barrier to ensure the readout happens after all writing 13 | let barrier = Arc::new(Barrier::new(numthreads + 1)); 14 | 15 | for i in 0..numthreads { 16 | let my_barrier = barrier.clone(); 17 | let my_lock = my_mutex.clone(); 18 | std::thread::spawn(move || { 19 | let mut guard = my_lock.lock().unwrap(); 20 | *guard += 1; 21 | 22 | // Release the lock to prevent a deadlock 23 | drop(guard); 24 | println!("thread {} is ready", i); 25 | // Blocks the current thread until all threads have rendezvoused here. 26 | my_barrier.wait(); 27 | println!("thread {} is done", i) 28 | }); 29 | } 30 | 31 | // A barrier will block `n`-1 threads which call [`wait()`] and then wake 32 | // up all threads at once when the `n`th thread calls [`wait()`]. 33 | barrier.wait(); 34 | 35 | let answer = { *my_mutex.lock().unwrap() }; 36 | assert_eq!(answer, numthreads); 37 | } 38 | -------------------------------------------------------------------------------- /并发编程/examples/deadlock.rs: -------------------------------------------------------------------------------- 1 | use lazy_static::lazy_static; 2 | use std::{fmt::Display, sync::Mutex}; 3 | 4 | #[derive(Debug)] 5 | #[allow(dead_code)] 6 | struct Config { 7 | host: String, 8 | port: u32, 9 | name: &'static str, 10 | } 11 | 12 | const NAME: &'static str = "name"; 13 | 14 | #[allow(dead_code)] 15 | impl Config { 16 | fn get_static_str(&self) -> &'static str { 17 | self.name 18 | } 19 | fn get_host_ref(&self) -> &String { 20 | &self.host 21 | } 22 | } 23 | 24 | lazy_static! { 25 | static ref LOCK1: Mutex = Mutex::new(Config { 26 | host: String::from("host"), 27 | port: 1234, 28 | name: NAME, 29 | }); 30 | } 31 | 32 | lazy_static! { 33 | static ref LOCK2: Mutex = Mutex::new(String::from("hello")); 34 | } 35 | 36 | // 用完后要及时让锁离开作用域,养成好习惯。 37 | fn _bad_example1() { 38 | let s = LOCK2.lock().unwrap(); 39 | println!("s = {}", s); 40 | // do something else 41 | } 42 | 43 | fn _good_example1() { 44 | { 45 | let s = LOCK2.lock().unwrap(); 46 | println!("s = {}", s); 47 | } 48 | // do something else 49 | } 50 | 51 | // 临时变量无变量绑定,语句结束立即析构,锁会立即被释放 52 | fn _good_example2() { 53 | let s = LOCK1.lock().unwrap().host.clone(); 54 | println!("s = {}", s); 55 | // do something else 56 | } 57 | 58 | fn _bad_example2() { 59 | println!("bad example2"); 60 | // 虽然临时变量会直接析构,但是锁的作用域依然会持续到函数的结束, 61 | // 因此请不要在同一个函数的参数中多次获取锁,即使是不同的锁(鬼知道他们之间会不会有关联)。 62 | _foo(LOCK1.lock().unwrap().port, LOCK1.lock().unwrap().port); 63 | println!("this will never be printed"); 64 | } 65 | 66 | fn _bad_example22() { 67 | println!("bad example22"); 68 | // 虽然临时变量会直接析构,但是锁的作用域依然会持续到match块的结束, 69 | // 同样的, if let 和 while let 也会造成同样的问题 70 | match LOCK1.lock().unwrap().host.is_empty() { 71 | true => println!("empty"), 72 | false => { 73 | println!("not empty"); 74 | println!("host = {}", LOCK1.lock().unwrap().host); // 这里会死锁 75 | println!("this will never be printed"); 76 | } 77 | } 78 | } 79 | 80 | fn _bad_example222() { 81 | println!("bad example222"); 82 | // if不会像match一样,这里锁的作用域在进入if块后就结束了。 83 | // 但仍然不建议在if块中获取锁,不写含糊不清的代码,养成好习惯。 84 | if LOCK1.lock().unwrap().host.is_empty() { 85 | println!("not empty"); 86 | println!("host = {}", LOCK1.lock().unwrap().host); // 这里不会死锁 87 | } else if LOCK1.lock().unwrap().port != 0 { 88 | println!("port = {}", LOCK1.lock().unwrap().port); // 这里不会死锁 89 | } 90 | } 91 | 92 | // 通过锁获取到的引用,会一直持有锁,直到引用离开作用域。 93 | fn _bad_example3() { 94 | println!("bad example3"); 95 | let s: &String = &LOCK1.lock().unwrap().host; // 这里的锁会一直持有,因为这里没有产生临时变量。 96 | println!("s = {}", s); 97 | LOCK1.lock().unwrap().host.push_str("xxxxxxxxxxxxxx"); // 这里会死锁 98 | } 99 | 100 | // 通过临时变量的锁获取到的'static生命周期的引用,不会造成死锁。 101 | // 虽然如此,但还是建议只要发现获取到引用,就把锁放进一个单独的作用域,养成好习惯。 102 | fn _good_example3() { 103 | println!("good example3"); 104 | let s = LOCK1.lock().unwrap().get_static_str(); 105 | println!("s = {}", s); 106 | LOCK1.lock().unwrap().host.push_str("xxxxxxxxxxxxxx"); 107 | } 108 | 109 | fn _foo(s: T1, s2: T2) -> Config { 110 | println!("s = {}{}", s, s2); 111 | Config { 112 | host: String::from("host"), 113 | port: 1234, 114 | name: NAME, 115 | } 116 | } 117 | 118 | fn main() { 119 | println!("Hello, world!"); 120 | _good_example1(); 121 | _good_example2(); 122 | _good_example3(); 123 | } 124 | -------------------------------------------------------------------------------- /并发编程/examples/main5.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("Hello, world!"); 3 | } 4 | -------------------------------------------------------------------------------- /并发编程/src/config.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | use std::sync::RwLock; 3 | use std::sync::RwLockReadGuard; 4 | use std::sync::RwLockWriteGuard; 5 | 6 | #[derive(Clone, Debug)] 7 | pub struct Config { 8 | pub ip: String, 9 | pub port: u16, 10 | } 11 | 12 | impl Config { 13 | pub fn new(ip: String, port: u16) -> Self { 14 | Self { ip, port } 15 | } 16 | 17 | pub fn access(self) -> RWAccess { 18 | RWAccess { 19 | config: Arc::new(RwLock::new(self)), 20 | _phantom: std::marker::PhantomData, 21 | } 22 | } 23 | 24 | #[allow(dead_code)] 25 | pub fn read_only(self) -> RWAccess { 26 | self.access() 27 | } 28 | 29 | pub fn read_write(self) -> RWAccess { 30 | self.access() 31 | } 32 | } 33 | 34 | #[derive(Clone, Debug)] 35 | pub struct ReadAccess; 36 | #[derive(Clone, Debug)] 37 | pub struct WriteAccess; 38 | 39 | // 特性定义,用于抽象读写操作 40 | pub(crate) trait AccessMode {} 41 | 42 | // pub(crate)是一个可见性修饰符,用来指示一个项(如函数、结构体、枚举、模块等)是公共的, 43 | // 但只在当前crate(包)内。Rust中的crate是一个编译单元,可以是一个库或一个可执行文件。 44 | // 当你使用pub(crate)修饰一个项时,这意味着这个项对整个crate内是可见的, 45 | // 但对于这个crate之外的代码则是私有的。这在你想要隐藏实现细节, 46 | // 但在crate内部的多个模块间共享代码时非常有用。 47 | 48 | impl AccessMode for ReadAccess {} 49 | impl AccessMode for WriteAccess {} 50 | 51 | #[derive(Clone, Debug)] 52 | pub struct RWAccess { 53 | config: Arc>, 54 | _phantom: std::marker::PhantomData, 55 | } 56 | 57 | // 读写的公共方法 58 | impl RWAccess { 59 | pub fn read(&self) -> RwLockReadGuard<'_, T> { 60 | self.config.read().unwrap() 61 | } 62 | 63 | pub fn clone_reader(&self) -> RWAccess { 64 | RWAccess { 65 | config: self.config.clone(), 66 | _phantom: std::marker::PhantomData, 67 | } 68 | } 69 | } 70 | 71 | // 写的私有方法 72 | impl RWAccess { 73 | pub fn write(&self) -> RwLockWriteGuard<'_, T> { 74 | self.config.write().unwrap() 75 | } 76 | } 77 | 78 | #[cfg(test)] 79 | mod tests { 80 | use super::*; 81 | 82 | #[test] 83 | fn test_config() { 84 | let config = Config::new("192.168.1.111".to_string(), 8080); 85 | let cnf_writer = config.read_write(); 86 | assert_eq!(cnf_writer.read().ip, "192.168.1.111"); 87 | 88 | let config_reader = cnf_writer.clone_reader(); 89 | 90 | cnf_writer.write().ip = "hhhhh".to_string(); 91 | assert_eq!(config_reader.read().ip, "hhhhh"); 92 | 93 | let cnf_writer2 = cnf_writer.clone(); 94 | let config_reader2 = config_reader.clone(); 95 | assert_eq!(cnf_writer2.read().ip, config_reader2.read().ip, "hhhhh"); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /异步编程/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /异步编程/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "异步编程" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | futures = "0.3.5" 10 | rand = "0.8.5" 11 | tokio = { version = "0.2", features = ["full"] } 12 | timer_future = { path = "E:/Doraemon/IT/Rust/study/timer_future" } 13 | pin-utils = "0.1.0" 14 | async-trait = "0.1" 15 | -------------------------------------------------------------------------------- /循环引用与自引用/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /循环引用与自引用/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "Inflector" 7 | version = "0.11.4" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" 10 | 11 | [[package]] 12 | name = "aliasable" 13 | version = "0.1.3" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" 16 | 17 | [[package]] 18 | name = "ouroboros" 19 | version = "0.15.6" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "e1358bd1558bd2a083fed428ffeda486fbfb323e698cdda7794259d592ca72db" 22 | dependencies = [ 23 | "aliasable", 24 | "ouroboros_macro", 25 | ] 26 | 27 | [[package]] 28 | name = "ouroboros_macro" 29 | version = "0.15.6" 30 | source = "registry+https://github.com/rust-lang/crates.io-index" 31 | checksum = "5f7d21ccd03305a674437ee1248f3ab5d4b1db095cf1caf49f1713ddf61956b7" 32 | dependencies = [ 33 | "Inflector", 34 | "proc-macro-error", 35 | "proc-macro2", 36 | "quote", 37 | "syn", 38 | ] 39 | 40 | [[package]] 41 | name = "proc-macro-error" 42 | version = "1.0.4" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 45 | dependencies = [ 46 | "proc-macro-error-attr", 47 | "proc-macro2", 48 | "quote", 49 | "syn", 50 | "version_check", 51 | ] 52 | 53 | [[package]] 54 | name = "proc-macro-error-attr" 55 | version = "1.0.4" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 58 | dependencies = [ 59 | "proc-macro2", 60 | "quote", 61 | "version_check", 62 | ] 63 | 64 | [[package]] 65 | name = "proc-macro2" 66 | version = "1.0.56" 67 | source = "registry+https://github.com/rust-lang/crates.io-index" 68 | checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" 69 | dependencies = [ 70 | "unicode-ident", 71 | ] 72 | 73 | [[package]] 74 | name = "quote" 75 | version = "1.0.26" 76 | source = "registry+https://github.com/rust-lang/crates.io-index" 77 | checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" 78 | dependencies = [ 79 | "proc-macro2", 80 | ] 81 | 82 | [[package]] 83 | name = "syn" 84 | version = "1.0.109" 85 | source = "registry+https://github.com/rust-lang/crates.io-index" 86 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 87 | dependencies = [ 88 | "proc-macro2", 89 | "quote", 90 | "unicode-ident", 91 | ] 92 | 93 | [[package]] 94 | name = "unicode-ident" 95 | version = "1.0.8" 96 | source = "registry+https://github.com/rust-lang/crates.io-index" 97 | checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" 98 | 99 | [[package]] 100 | name = "version_check" 101 | version = "0.9.4" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 104 | 105 | [[package]] 106 | name = "循环引用与自引用" 107 | version = "0.1.0" 108 | dependencies = [ 109 | "ouroboros", 110 | ] 111 | -------------------------------------------------------------------------------- /循环引用与自引用/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "循环引用与自引用" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | ouroboros = "0.15.6" 10 | -------------------------------------------------------------------------------- /所有权和借用/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /所有权和借用/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "所有权和借用" 7 | version = "0.1.0" 8 | -------------------------------------------------------------------------------- /所有权和借用/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "所有权和借用" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /所有权和借用/src/main.rs: -------------------------------------------------------------------------------- 1 | mod study; 2 | 3 | // Rust 中每一个值都被一个变量所拥有,该变量被称为值的所有者。 4 | // 一个值同时只能被一个变量所拥有,或者说一个值只能拥有一个所有者 5 | // 当所有者(变量)离开作用域范围时,这个值将被丢弃(drop) 6 | fn main() { 7 | println!("--------------------初识所有权---------------------"); 8 | // hello分配在堆上,s1获得这个String的所有权 9 | let s1 = String::from("hello"); 10 | // s2获得s1指向的String的所有权,s1将不能再访问这个String 11 | let s2 = s1; 12 | // println!("{}, world!", s1); // error: value borrowed here after move 13 | println!("{}, world!", s2); 14 | 15 | test_func1(); 16 | println!("--------------------深拷贝---------------------"); 17 | // Rust 永远也不会自动创建数据的 “深拷贝”。因此,任何自动的复制都不是深拷贝。 18 | // 如果我们确实需要深度复制 String 中堆上的数据,而不仅仅是栈上的数据,可以使用一个叫做 clone 的方法。 19 | let s3 = String::from("hello"); 20 | let s4 = s3.clone(); 21 | println!("s3 = {}, s4 = {}", s3, s4); 22 | 23 | // 浅拷贝 24 | // 浅拷贝只发生在栈上,因此性能很高,在日常编程中,浅拷贝无处不在。 25 | println!("--------------------浅拷贝---------------------"); 26 | // 代码背后的逻辑很简单, 将 `5` 绑定到变量 `x`;接着拷贝 `x` 的值赋给 `y`,最终 `x` 和 `y` 都等于 `5`, 27 | // 因为整数是 Rust 基本数据类型,是固定大小的简单值,因此这两个值都是通过自动拷贝的方式来赋值的, 28 | // 都被存在栈中,完全无需在堆上分配内存。 29 | let x = 5; 30 | let y = x; 31 | println!("x = {}, y = {}", x, y); 32 | // 但这段代码似乎与我们刚刚学到的内容相矛盾:没有调用 clone, 33 | // 不过依然实现了类似深拷贝的效果 —— 没有报所有权的错误。 34 | // 原因是像整型这样的基本类型在编译时是已知大小的,会被存储在栈上,所以拷贝其实际的值是快速的。 35 | // 换句话说,这里没有深浅拷贝的区别,因此这里调用 clone`并不会与通常的浅拷贝有什么不同, 36 | // 我们可以不用管它(可以理解成在栈上做了深拷贝)。 37 | // Rust 有一个叫做 Copy 的特征,可以用在类似整型这样在栈中存储的类型。 38 | // 如果一个类型拥有 Copy 特征,一个旧的变量在被赋值给其他变量后仍然可用。 39 | // 一个通用的规则:任何基本类型的组合可以 Copy ,不需要分配内存或某种形式资源的类型是可以 Copy 的。 40 | // 不可变引用 &T 可以 Copy ,但是注意: 可变引用 &mut T 是不可以 Copy的。 41 | let str1 = "hello"; 42 | let str2 = str1; 43 | println!("str1 = {}, str2 = {}", str1, str2); 44 | 45 | // 再识所有权 46 | study_ownership(); 47 | // 同样的,函数返回值也有所有权 48 | study_return_ownership(); 49 | // 引用和借用 50 | study::study_reference_and_borrowing(); 51 | } 52 | 53 | fn study_return_ownership() { 54 | println!("--------------------函数返回值所有权---------------------"); 55 | let s1 = gives_ownership(); // gives_ownership 将返回值 移给 s1 56 | println!("s1 = {}", s1); 57 | 58 | let s2 = String::from("hello2"); // s2 进入作用域 59 | 60 | let s3 = takes_and_gives_back(s2); // s2 被移动到 takes_and_gives_back 中 61 | println!("s3 = {}", s3); 62 | 63 | // 这里, s3 移出作用域并被丢弃。s2 也移出作用域,但已被移走,所以什么也不会发生。s1 移出作用域并被丢弃。 64 | } 65 | 66 | fn gives_ownership() -> String { 67 | // gives_ownership 将返回值移动给 68 | // 调用它的函数 69 | 70 | let some_string = String::from("hello1"); // some_string 进入作用域. 71 | 72 | some_string // 返回 some_string 并移出给调用的函数 73 | } 74 | 75 | // takes_and_gives_back 将传入字符串并返回该值 76 | fn takes_and_gives_back(a_string: String) -> String { 77 | // a_string 进入作用域 78 | 79 | a_string // 返回 a_string 并移出给调用的函数 80 | } 81 | 82 | fn study_ownership() { 83 | println!("--------------------再识所有权---------------------"); 84 | let s = String::from("hello"); // s 进入作用域 85 | takes_ownership(s); // s 的值移动到函数里 ... 86 | // ... 所以到这里不再有效 87 | //println!("s = {}", s); // error: value borrowed here after move 88 | 89 | let x = 5; // x 进入作用域 90 | makes_copy(x); // x 应该移动函数里, 91 | // 但 i32 是 Copy 的,所以在后面可继续使用 x 92 | 93 | // 函数结束后 x 先移出了作用域,然后是 s。但因为 s 的值已被移走,所以不会有特殊操作 94 | } 95 | 96 | fn takes_ownership(some_string: String) { 97 | // some_string 进入作用域 98 | println!("{}", some_string); 99 | } // 这里,some_string 移出作用域并调用 `drop` 方法。占用的内存被释放 100 | 101 | fn makes_copy(some_integer: i32) { 102 | // some_integer 进入作用域 103 | println!("{}", some_integer); 104 | } // 这里,some_integer 移出作用域。不会有特殊操作 105 | 106 | fn test_func1() { 107 | println!("--------------------引用---------------------"); 108 | // 这段代码和之前的 `String` 有一个本质上的区别: 109 | // 在 `String` 的例子中 `s1` 持有了通过`String::from("hello")` 创建的值的所有权, 110 | // 而这个例子中,`x` 只是引用了存储在二进制中的字符串 `"hello, world"`,并没有持有所有权。 111 | // 因此 `let y = x` 中,仅仅是对该引用进行了拷贝,此时 `y` 和 `x` 都引用了同一个字符串。 112 | let x: &str = "hello, world"; 113 | let y = x; 114 | println!("{},{}", x, y); 115 | // &str是不可变引用。 116 | } 117 | -------------------------------------------------------------------------------- /所有权和借用/src/study.rs: -------------------------------------------------------------------------------- 1 | // 总的来说,借用规则如下: 2 | // - 同一时刻,你只能拥有要么一个可变引用, 要么任意多个不可变引用 3 | // - 引用必须总是有效的 4 | 5 | pub fn study_reference_and_borrowing() { 6 | println!("--------------------引用(借用)---------------------"); 7 | // 如果仅仅支持通过转移所有权的方式获取一个值,那会让程序变得复杂。 8 | // Rust 能否像其它编程语言一样,使用某个变量的指针或者引用呢?答案是可以。 9 | let x = 5; 10 | // & 符号即是引用,它们允许你使用值,但是不获取所有权 11 | let y = &x; // y 是 x 的引用 12 | assert_eq!(5, x); 13 | assert_eq!(5, *y); // 使用 *y 来解出引用所指向的值(也就是解引用) 14 | 15 | // 不可变引用 16 | study_immutable_reference(); 17 | // 可变引用 18 | study_mutable_reference(); 19 | // 可变引用与不可变引用不能同时存在 20 | study_mutable_and_immutable_reference(); 21 | // 悬垂引用(Dangling References) 22 | study_dangling_references(); 23 | } 24 | 25 | fn study_dangling_references() { 26 | println!("--------------------悬垂引用(Dangling References)---------------------"); 27 | // 悬垂引用也叫做悬垂指针,意思为指针指向某个值后,这个值被释放掉了,而指针仍然存在, 28 | // 其指向的内存可能不存在任何值或已被其它变量重新使用。 29 | 30 | // 在 Rust 中编译器可以确保引用永远也不会变成悬垂状态: 31 | // 当你获取数据的引用后,编译器可以确保数据不会在引用结束前被释放,要想释放数据,必须先停止其引用的使用 32 | 33 | // 让我们尝试创建一个悬垂引用,Rust 会抛出一个编译时错误。 34 | // let reference_to_nothing = dangle(); 35 | 36 | // 其中一个很好的解决方法是直接返回 String。 最终 String 的 所有权被转移给外面的调用者。 37 | let s = no_dangle(); 38 | println!("s = {}", s); 39 | } 40 | /* 41 | // 让我们尝试创建一个悬垂引用,Rust 会抛出一个编译时错误 42 | fn dangle() -> &String { 43 | // dangle 返回一个字符串的引用 44 | 45 | let s = String::from("hello"); // s 是一个新字符串 46 | 47 | &s // 返回字符串 s 的引用 48 | } // 这里 s 离开作用域并被丢弃。其内存被释放。 49 | // 危险! 50 | */ 51 | 52 | fn no_dangle() -> String { 53 | let s = String::from("hello"); 54 | s 55 | } 56 | 57 | fn study_mutable_and_immutable_reference() { 58 | println!("--------------------可变引用与不可变引用不能同时存在---------------------"); 59 | // 可变引用与不可变引用不能同时存在 60 | let mut _s = String::from("hello"); 61 | let r1 = &_s; // 没问题 62 | let r2 = &_s; // 没问题 63 | // 无法借用可变 _s 因为它已经被借用了不可变 64 | // let r3 = &mut _s; // 大问题 65 | 66 | println!("{}, {}", r1, r2); 67 | 68 | // 引用的作用域从创建开始,一直持续到它最后一次使用的地方,这个跟变量的作用域有所不同, 69 | // 变量的作用域从创建持续到某一个花括号 `}`。 70 | let mut s = String::from("hello"); 71 | 72 | let r1 = &s; 73 | let r2 = &s; 74 | println!("{} and {}", r1, r2); 75 | // 新编译器中,r1,r2作用域在这里结束 76 | 77 | let r3 = &mut s; 78 | println!("{}", r3); 79 | // 新编译器中,r3作用域在这里结束 80 | 81 | // 对于这种编译器优化行为,Rust 专门起了一个名字 —— Non-Lexical Lifetimes(NLL) , 82 | // 专门用于找到某个引用在作用域(`}`)结束前就不再被使用的代码位置。 83 | 84 | // 值得注意的是,NLL仅影响借用检查,并不会改变对象的实际生命周期。 85 | // Rust 广泛使用 RAII,因此一些对象的实现,例如锁,必须具有确定的可预测的生命周期。 86 | // NLL 没有改变这些对象的生命周期,因此它们的析构函数在与之前完全相同的点执行:在它们的词法作用域的末尾。 87 | fn nll_example() { 88 | struct Foo { 89 | i: i32, 90 | } 91 | impl Drop for Foo { 92 | fn drop(&mut self) { 93 | println!("drop foo {}", self.i); 94 | } 95 | } 96 | // 临时变量无变量绑定,语句结束立即析构 97 | (&mut Foo { i: 100 }).i += 1; // 这里创建了一个临时变量,drop函数立即执行 98 | let x = &mut Foo { i: 1 }; 99 | x.i = 3; 100 | println!("x.x = {}", x.i); //尽管x的作用域在这里结束,但是drop函数在这里并没有执行 101 | let y = &mut Foo { i: 2 }; 102 | y.i = 4; 103 | println!("y.x = {}", y.i); 104 | // drop foo 101 105 | // x.x = 3 106 | // y.x = 4 107 | // drop foo 4 108 | // drop foo 3 109 | } 110 | nll_example(); 111 | } 112 | 113 | fn study_mutable_reference() { 114 | println!("--------------------可变引用---------------------"); 115 | let mut s = String::from("hello"); 116 | 117 | change(&mut s); 118 | println!("s = {}", s); 119 | // 可变引用并不是随心所欲、想用就用的,它有一个很大的限制: 同一作用域,特定数据只能有一个可变引用。 120 | // 这种限制的好处就是使 Rust 在编译期就避免数据竞争。 121 | let r1 = &mut s; 122 | // let r2 = &mut s; // cannot borrow `s` as mutable more than once at a time 123 | r1.push_str(", it's me"); 124 | println!("s = {}", s); 125 | 126 | // 很多时候,大括号可以帮我们解决一些编译不通过的问题,通过手动限制变量的作用域。 127 | { 128 | let r2 = &mut s; 129 | r2.push_str("🤣"); 130 | // r2 在这里离开了作用域,所以我们完全可以创建一个新的引用 131 | } 132 | let r1 = &mut s; 133 | println!("s = {}", r1); 134 | } 135 | 136 | fn change(some_string: &mut String) { 137 | some_string.push_str(", world"); 138 | } 139 | 140 | fn study_immutable_reference() { 141 | // 若存在不可变引用,即使原始变量拥有所有权,也不能再创建可变引用(即引用的值不会被修改) 142 | println!("--------------------不可变引用---------------------"); 143 | let s1 = String::from("hello"); 144 | 145 | let len = calculate_length(&s1); 146 | 147 | println!("The length of '{}' is {}.", s1, len); 148 | } 149 | 150 | fn calculate_length(s: &String) -> usize { 151 | // 正如变量默认不可变一样,引用指向的值默认也是不可变的 152 | // s.push_str(", world!"); //cannot borrow `*s` as mutable, as it is behind a `&` reference `s` is a `&` reference, 153 | 154 | s.len() 155 | // &s1 语法,我们创建了一个指向 s1 的引用,但是并不拥有它。 156 | // 因为并不拥有这个值,当引用离开作用域后,其指向的值也不会被丢弃。 157 | } 158 | -------------------------------------------------------------------------------- /文件操作/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /a 3 | -------------------------------------------------------------------------------- /文件操作/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "文件操作" 7 | version = "0.1.0" 8 | -------------------------------------------------------------------------------- /文件操作/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "文件操作" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /文件操作/hello.txt: -------------------------------------------------------------------------------- 1 | hello 2 | www.xxxx.cnwww.xxxx.cn -------------------------------------------------------------------------------- /文件操作/lorem_ipsum.txt: -------------------------------------------------------------------------------- 1 | xxxxxxxxxxxxxxxxxxxxxxxxx -------------------------------------------------------------------------------- /文件操作/src/hello.txt: -------------------------------------------------------------------------------- 1 | hello 2 | -------------------------------------------------------------------------------- /方法_泛型_特征/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /方法_泛型_特征/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "方法_泛型_特征" 7 | version = "0.1.0" 8 | -------------------------------------------------------------------------------- /方法_泛型_特征/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "方法_泛型_特征" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /方法_泛型_特征/src/inheritance.rs: -------------------------------------------------------------------------------- 1 | // 基类 2 | struct Animal { 3 | age: i32, 4 | } 5 | 6 | impl Animal { 7 | fn new(age: i32) -> Self { 8 | Animal { age } 9 | } 10 | 11 | // 基类方法,下面的代码中,它将会被子类复用 12 | fn speak(&self) { 13 | println!("I'm an animal. Age = {}", self.age); 14 | } 15 | } 16 | 17 | // 子类 18 | struct Dog { 19 | supper: Animal, 20 | _name: String, 21 | } 22 | 23 | impl Dog { 24 | fn new(name: String, age: i32) -> Self { 25 | Dog { 26 | supper: Animal::new(age), 27 | _name: name, 28 | } 29 | } 30 | 31 | // 实现"复用父类的方法" 32 | fn speak(&self) { 33 | self.supper.speak(); 34 | } 35 | } 36 | 37 | pub fn study_inheritance() { 38 | println!("----------------继承----------------"); 39 | // Rust 不支持继承,但可以通过子类嵌套父类的方式,实现类似“继承”的效果,跟Go语言挺像的。 40 | // Don't look for tricks to simulate inheritance. Rust isn't designed this way and 41 | // it doesn't help to try to make it OOP. 42 | // 尽量不要去模拟继承,Rust并不是为了这样设计的,也不会帮助你去尝试OOP。 43 | let dog = Dog::new(String::from("Tom"), 2); 44 | dog.speak(); 45 | } 46 | -------------------------------------------------------------------------------- /方法_泛型_特征/src/operator_overloading.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Add; 2 | 3 | // 为Point结构体派生Debug特征,用于格式化输出 4 | #[derive(Debug)] 5 | struct Point> { 6 | //限制类型T必须实现了Add特征,否则无法进行+操作。 7 | x: T, 8 | y: T, 9 | } 10 | 11 | impl> Add for Point { 12 | // fn add(self, rhs: Rhs) -> Self::Output; 13 | // 这个 trait 的定义中,`RHS` 是 `Right Hand Side` 的缩写,代表右操作数。 14 | // 这里的 `RHS=Self` 表示默认情况下右操作数是 实现该 trait 的类型本身。 15 | // 如果在实现该 trait 时指定了一个不同的类型作为右操作数,则可以使用另外的类型来替换 `RHS`。 16 | type Output = Point; 17 | 18 | // 运算符重载 19 | fn add(self, p: Point) -> Self { 20 | Point { 21 | x: self.x + p.x, 22 | y: self.y + p.y, 23 | } 24 | } 25 | } 26 | 27 | fn add>(a: T, b: T) -> T { 28 | a + b 29 | } 30 | 31 | pub fn study_example() { 32 | study_example1(); 33 | } 34 | 35 | fn study_example1() { 36 | println!("------------------运算符重载------------------"); 37 | let p1 = Point { 38 | x: 1.1f32, 39 | y: 1.1f32, 40 | }; 41 | let p2 = Point { 42 | x: 2.1f32, 43 | y: 2.1f32, 44 | }; 45 | println!("{:?}", add(p1, p2)); 46 | 47 | let p3 = Point { x: 1i32, y: 1i32 }; 48 | let p4 = Point { x: 2i32, y: 2i32 }; 49 | println!("p3: {:?}", p3); 50 | println!("p4: {:?}", p4); 51 | // 运算符重载 52 | println!("p3 + p4 = {:?}", p3 + p4); 53 | 54 | // 默认泛型类型参数(不同类型之间的运算符重载) 55 | println!("------------------默认泛型类型参数(不同类型之间的运算符重载)------------------"); 56 | let p5 = Point3D { 57 | x: 1i32, 58 | y: 1i32, 59 | z: 1i32, 60 | }; 61 | let p6 = Point2D { x: 2i32, y: 2i32 }; 62 | println!("p5: {:?}", p5); 63 | println!("p6: {:?}", p6); 64 | // 运算符重载 65 | println!("p5 + p6 = {:?}", p5 + p6); 66 | } 67 | 68 | // 三维坐标 69 | #[derive(Debug)] 70 | struct Point3D> { 71 | x: T, 72 | y: T, 73 | z: T, 74 | } 75 | 76 | #[derive(Debug)] 77 | struct Point2D { 78 | x: T, 79 | y: T, 80 | } 81 | 82 | // RHS = Point2D 83 | impl Add> for Point3D 84 | where 85 | T: Add, // 限制类型T必须实现了Add特征,否则无法进行+操作。 86 | { 87 | type Output = Point3D; 88 | 89 | fn add(self, p: Point2D) -> Self { 90 | Point3D { 91 | x: self.x + p.x, 92 | y: self.y + p.y, 93 | z: self.z, 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /方法_泛型_特征/src/polymorphism.rs: -------------------------------------------------------------------------------- 1 | pub trait Sound { 2 | fn sound(&self) -> String; 3 | } 4 | 5 | #[derive(Debug)] 6 | pub struct Cat { 7 | pub name: String, 8 | } 9 | 10 | impl Sound for Cat { 11 | fn sound(&self) -> String { 12 | format!("{}: miao miao miao~", self.name) 13 | } 14 | } 15 | 16 | pub struct Dog { 17 | pub name: String, 18 | } 19 | 20 | impl Sound for Dog { 21 | fn sound(&self) -> String { 22 | format!("{}: wang wang wang~", self.name) 23 | } 24 | } 25 | 26 | pub struct Pig { 27 | pub name: String, 28 | } 29 | 30 | impl Sound for Pig { 31 | fn sound(&self) -> String { 32 | format!("{}: ao ao ao~", self.name) 33 | } 34 | } 35 | 36 | fn make_sound(x: &impl Sound) -> String { 37 | x.sound() 38 | } 39 | 40 | // 虽然 `impl Trait` 这种语法非常好理解,但是实际上它只是一个语法糖, 41 | // 本质上是一个 `trait bound` 泛型。 42 | fn make_sound2(x: &T) -> String { 43 | x.sound() 44 | } 45 | 46 | fn multi_dog_sound(x: Vec<&impl Sound>) { 47 | println!("many dogs sound: "); 48 | for i in x { 49 | println!("{}", i.sound()); 50 | } 51 | } 52 | 53 | // Vec<&impl Sound> 等价于 泛型函数 54 | fn multi_dog_sound2(x: Vec<&T>) { 55 | println!("many dogs sound: "); 56 | for i in x { 57 | println!("{}", i.sound()); 58 | } 59 | } 60 | 61 | fn multi_animal_sound(x: Vec>) { 62 | println!("many animals sound: "); 63 | for i in x { 64 | println!("{}", i.sound()); 65 | } 66 | } 67 | 68 | pub fn study_polymorphism() { 69 | println!("----------------多态的例子----------------"); 70 | let cat = Cat { 71 | name: String::from("Tom"), 72 | }; 73 | let dog = Dog { 74 | name: String::from("Jack"), 75 | }; 76 | let pig = Pig { 77 | name: String::from("Piggy"), 78 | }; 79 | println!("{}", make_sound(&cat)); 80 | println!("{}", make_sound(&dog)); 81 | println!("{}", make_sound(&pig)); 82 | println!("{}", make_sound2(&pig)); 83 | 84 | let dog2 = Dog { 85 | name: String::from("snoopy"), 86 | }; 87 | let dog3 = Dog { 88 | name: String::from("haha"), 89 | }; 90 | // 多条狗的声音(同一类型,使用静态分发) 91 | println!("----------------多条狗的声音(同一类型,使用静态分发)----------------"); 92 | let dogs = vec![&dog, &dog2, &dog3]; 93 | multi_dog_sound(dogs.clone()); 94 | multi_dog_sound2(dogs); 95 | 96 | // vector中的元素类型不一致,无法使用静态分发 97 | // let animals = vec![&cat, &dog, &pig]; 98 | 99 | // 多种动物的声音(不同类型,使用动态分发) 100 | println!("----------------多种动物的声音(不同类型,使用动态分发)----------------"); 101 | let mut x: Vec> = Vec::new(); 102 | x.push(Box::new(cat)); 103 | x.push(Box::new(dog)); 104 | x.push(Box::new(pig)); 105 | multi_animal_sound(x); 106 | } 107 | -------------------------------------------------------------------------------- /日志与监控/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /logs/ 3 | -------------------------------------------------------------------------------- /日志与监控/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "日志与监控" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | log = "0.4" 10 | tracing = "0.1" 11 | tracing-subscriber = { version = "0.3", features = [ 12 | "env-filter", 13 | "fmt", 14 | "time", 15 | "local-time", 16 | ] } 17 | env_logger = "0.10" 18 | serde_json = "1.0" 19 | serde = { version = "1.0", features = ["derive"] } 20 | hex = "0.4" 21 | tokio = { version = "1.0", features = ["full"] } 22 | tracing-appender = "0.2" 23 | color-eyre = "0.6" 24 | tracing-error = "0.2.0" 25 | chrono = "0.4.31" 26 | 27 | [[example]] 28 | name = "example1" 29 | path = "examples/*" 30 | -------------------------------------------------------------------------------- /日志与监控/examples/main1.rs: -------------------------------------------------------------------------------- 1 | use log::{Level, debug, error, info, log_enabled, trace, warn}; 2 | 3 | pub fn shave_the_yak(yak: &mut Yak) { 4 | trace!("Commencing yak shaving"); 5 | 6 | loop { 7 | match find_a_razor() { 8 | Ok(razor) => { 9 | info!("Razor located: {:?}", razor); 10 | yak.shave(razor); 11 | break; 12 | } 13 | Err(err) => { 14 | warn!("Unable to locate a razor: {}, retrying", err); 15 | } 16 | } 17 | std::thread::sleep(std::time::Duration::from_secs(1)); 18 | } 19 | } 20 | 21 | pub struct Yak { 22 | name: String, 23 | } 24 | 25 | impl Yak { 26 | pub fn shave(&mut self, razor: Razor) { 27 | println!("Shaving yak {:?} with {:?}", self.name, razor); 28 | } 29 | } 30 | #[derive(Debug)] 31 | pub struct Razor; 32 | 33 | fn find_a_razor() -> Result { 34 | Err("Could not find a razor".to_string()) 35 | } 36 | 37 | fn main() { 38 | env_logger::builder() 39 | .filter_level(log::LevelFilter::Trace) 40 | .init(); 41 | debug!("this is a debug {}", "message"); 42 | error!("this is printed by default"); 43 | 44 | if log_enabled!(Level::Info) { 45 | let x = 3 * 4; // expensive computation 46 | info!("the answer was: {}", x); 47 | } 48 | let mut yak = Yak { 49 | name: "Fred".to_string(), 50 | }; 51 | shave_the_yak(&mut yak); 52 | } 53 | -------------------------------------------------------------------------------- /日志与监控/examples/main3.rs: -------------------------------------------------------------------------------- 1 | use color_eyre::{Result, eyre::eyre}; 2 | use tracing::{error, info, instrument}; 3 | use tracing_appender::rolling; 4 | use tracing_error::ErrorLayer; 5 | 6 | use tracing_subscriber::{ 7 | Registry, filter::EnvFilter, fmt, layer::SubscriberExt, util::SubscriberInitExt, 8 | }; 9 | 10 | #[instrument] 11 | fn return_err() -> Result<()> { 12 | Err(eyre!("Something went wrong")) 13 | } 14 | 15 | #[instrument] 16 | fn call_return_err() { 17 | info!("going to log error"); 18 | if let Err(err) = return_err() { 19 | // 推荐大家运行下,看看这里的输出效果 20 | error!(?err, "error"); 21 | } 22 | } 23 | // 最后,再来看一个综合的例子,使用了 color-eyre 和 文件输出,前者用于为输出的日志加上更易读的颜色。 24 | fn main() -> Result<()> { 25 | let env_filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")); 26 | // 输出到控制台stderr中 27 | let formatting_layer = fmt::layer().pretty().with_writer(std::io::stderr); 28 | 29 | // 输出到文件中 30 | let file_name = chrono::Local::now() 31 | .format("app-%Y-%m-%d-%H-%M-%S.log") 32 | .to_string(); 33 | println!("file_name: {}", file_name); 34 | let file_appender = rolling::never("logs", file_name); 35 | let (non_blocking_appender, _guard) = tracing_appender::non_blocking(file_appender); 36 | let file_layer = fmt::layer() 37 | .with_ansi(false) 38 | .with_writer(non_blocking_appender); 39 | 40 | // 注册 41 | Registry::default() 42 | .with(env_filter) 43 | // ErrorLayer 可以让 color-eyre 获取到 span 的信息 44 | .with(ErrorLayer::default()) 45 | .with(formatting_layer) 46 | .with(file_layer) 47 | .init(); 48 | 49 | // 安裝 color-eyre 的 panic 处理句柄 50 | color_eyre::install()?; 51 | 52 | call_return_err(); 53 | 54 | Ok(()) 55 | } 56 | 57 | //TODO: https://course.rs/logs/tracing-logger.html 58 | -------------------------------------------------------------------------------- /日志与监控/examples/main4.rs: -------------------------------------------------------------------------------- 1 | use tracing::{Level, info}; 2 | 3 | fn main() { 4 | let subscriber = tracing_subscriber::FmtSubscriber::builder() 5 | // all spans/events with a level higher than TRACE (e.g, debug, info, warn, etc.) 6 | // will be written to stdout. 7 | .with_max_level(Level::TRACE) 8 | // builds the subscriber. 9 | .finish(); 10 | 11 | tracing::subscriber::with_default(subscriber, || { 12 | info!("This will be logged to stdout"); 13 | }); 14 | info!("This will _not_ be logged to stdout"); 15 | 16 | let subscriber2 = tracing_subscriber::FmtSubscriber::builder() 17 | .with_max_level(Level::TRACE) 18 | .finish(); 19 | { 20 | let _scope = tracing::subscriber::set_default(subscriber2); 21 | info!("This will be logged to stdout"); 22 | } 23 | info!("This will _not_ be logged to stdout"); 24 | println!("------------------ this is a line ------------------"); 25 | // let file_appender = tracing_appender::rolling::RollingFileAppender::new( 26 | // tracing_appender::rolling::Rotation::HOURLY, 27 | // "./logs", 28 | // "prefix", 29 | // ); 30 | let file_appender = tracing_appender::rolling::minutely("./logs", "prefix"); 31 | let (non_blocking_appender, _writer_guard) = tracing_appender::non_blocking(file_appender); 32 | let subscriber = tracing_subscriber::FmtSubscriber::builder() 33 | .with_max_level(Level::TRACE) 34 | .with_writer(non_blocking_appender) 35 | .with_timer(tracing_subscriber::fmt::time::LocalTime::rfc_3339()) 36 | .finish(); 37 | tracing::subscriber::set_global_default(subscriber).expect("setting default subscriber failed"); 38 | info!("This will _not_ be logged to stdout, but to a file"); 39 | log::info!("This will _not_ be logged to stdout"); 40 | tracing::trace!("This will be logged to stdout, but to a file"); 41 | // std::thread::sleep(std::time::Duration::from_secs(60)); 42 | } 43 | -------------------------------------------------------------------------------- /日志与监控/examples/main5.rs: -------------------------------------------------------------------------------- 1 | use tracing::{Level, info}; 2 | use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt}; 3 | fn main() { 4 | let subscriber = tracing_subscriber::FmtSubscriber::builder() 5 | // all spans/events with a level higher than TRACE (e.g, debug, info, warn, etc.) 6 | // will be written to stdout. 7 | .with_max_level(Level::TRACE) 8 | // builds the subscriber. 9 | .finish(); 10 | 11 | tracing::subscriber::with_default(subscriber, || { 12 | info!("This will be logged to stdout"); 13 | }); 14 | info!("This will _not_ be logged to stdout"); 15 | 16 | println!("------------------ this is a line ------------------"); 17 | 18 | let subscriber2 = tracing_subscriber::FmtSubscriber::builder() 19 | .with_max_level(Level::TRACE) 20 | .finish(); 21 | { 22 | let _scope = tracing::subscriber::set_default(subscriber2); 23 | info!("This will be logged to stdout"); 24 | } 25 | info!("This will _not_ be logged to stdout"); 26 | 27 | println!("------------------ this is a line ------------------"); 28 | 29 | log::info!("This will _not_ be logged to stdout"); 30 | tracing::info!("This will _not_ be logged to stdout"); 31 | { 32 | let layered = tracing_subscriber::registry().with(fmt::layer().with_thread_names(true)); 33 | let _scope = tracing::subscriber::set_default(layered); 34 | log::info!("This will _not_ be logged to stdout"); 35 | tracing::trace!("This will be logged to stdout"); 36 | } 37 | println!("------------------ this is a line ------------------"); 38 | { 39 | let layered = tracing_subscriber::registry().with(fmt::layer().with_thread_names(true)); 40 | let _scope = layered.set_default(); 41 | log::info!("This will be logged to stdout"); 42 | tracing::trace!("This will be logged to stdout"); 43 | } 44 | log::info!("This will _not_ be logged to stdout"); 45 | tracing::trace!("This will _not_ be logged to stdout"); 46 | } 47 | -------------------------------------------------------------------------------- /日志与监控/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("Hello, world!"); 3 | } 4 | -------------------------------------------------------------------------------- /智能指针/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /智能指针/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "智能指针" 7 | version = "0.1.0" 8 | -------------------------------------------------------------------------------- /智能指针/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "智能指针" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /智能指针/src/cow.rs: -------------------------------------------------------------------------------- 1 | pub fn study_cow() { 2 | println!("---------------------------------study Cow---------------------------------"); 3 | use std::borrow::Cow; 4 | // Cow是Rust提供的用于实现写时克隆(Clone on write)的智能指针。 5 | // 从Cow的定义看,它是一个enum,包含一个对类型B的只读引用,或者包含一个拥有类型B的所有权的数据。 6 | // 使用Cow使我们在返回数据时提供了两种可能: 7 | // 要么返回一个借用的数据(只读),要么返回一个拥有所有权的数据(可读写)。 8 | // 使用Cow主要用来减少内存的分配和复制,因为绝大多数的场景都是读多写少。 9 | // 使用Cow可以在需要些的时候才做一次内存复制,这样就很大程度减少了内存复制次数。 10 | fn abs_all(input: &mut Cow<'_, [i32]>) { 11 | for i in 0..input.len() { 12 | let v = input[i]; 13 | if v < 0 { 14 | // Clones into a vector if not already owned. 15 | input.to_mut()[i] = -v; 16 | } 17 | } 18 | } 19 | 20 | // No clone occurs because `input` doesn't need to be mutated. 21 | let slice = [0, 1, 2]; 22 | let mut input = Cow::from(&slice[..]); 23 | abs_all(&mut input); 24 | 25 | // Clone occurs because `input` needs to be mutated. 26 | let slice = [-1, 0, 1]; 27 | let mut input = Cow::from(&slice[..]); 28 | abs_all(&mut input); 29 | 30 | // No clone occurs because `input` is already owned. 31 | let mut input = Cow::from(vec![-1, 0, 1]); 32 | abs_all(&mut input); 33 | 34 | // Another example showing how to keep Cow in a struct: 35 | struct Items<'a, X> 36 | where 37 | [X]: ToOwned>, 38 | { 39 | values: Cow<'a, [X]>, 40 | } 41 | 42 | impl<'a, X: Clone + 'a> Items<'a, X> 43 | where 44 | [X]: ToOwned>, 45 | { 46 | fn new(v: Cow<'a, [X]>) -> Self { 47 | Items { values: v } 48 | } 49 | } 50 | 51 | // Creates a container from borrowed values of a slice 52 | let readonly = [1, 2]; 53 | let borrowed = Items::new(Cow::Borrowed(&readonly[..])); 54 | match borrowed { 55 | Items { 56 | values: Cow::Borrowed(b), 57 | } => println!("borrowed {b:?}"), 58 | _ => panic!("expect borrowed value"), 59 | } 60 | 61 | let mut clone_on_write = borrowed; 62 | // Mutates the data from slice into owned vec and pushes a new value on top 63 | clone_on_write.values.to_mut().push(3); 64 | println!("clone_on_write = {:?}", clone_on_write.values); 65 | 66 | // The data was mutated. Let's check it out. 67 | match clone_on_write { 68 | Items { 69 | values: Cow::Owned(_), 70 | } => println!("clone_on_write contains owned data"), 71 | _ => panic!("expect owned data"), 72 | } 73 | 74 | // 一个例子 75 | cow_example(); 76 | } 77 | 78 | fn cow_example() { 79 | println!("---------------------------------cow_example---------------------------------"); 80 | use std::borrow::Cow; 81 | // 实现一个字符串敏感词替换函数,从给定的字符串替换掉预制的敏感词。 82 | const SENSITIVE_WORD: &str = "bad"; 83 | 84 | fn remove_sensitive_word<'a>(words: &'a str) -> Cow<'a, str> { 85 | if words.contains(SENSITIVE_WORD) { 86 | Cow::Owned(words.replace(SENSITIVE_WORD, "")) 87 | } else { 88 | Cow::Borrowed(words) 89 | } 90 | } 91 | fn remove_sensitive_word_old(words: &str) -> String { 92 | if words.contains(SENSITIVE_WORD) { 93 | words.replace(SENSITIVE_WORD, "") 94 | } else { 95 | words.to_owned() 96 | } 97 | } 98 | // 例子中给出了remove_sensitive_word和remove_sensitive_word_old两种实现, 99 | // 前者的返回值使用了Cow,后者返回值使用的是String。仔细分析一下,很明显前者的实现效率更高。 100 | // 因为如果输入的字符串中没有敏感词时,前者Cow::Borrowed(words)不会发生堆内存的分配和拷贝, 101 | // 后者words.to_owned()会发生一次堆内存的分配和拷贝。 102 | 103 | let words = "I'm a bad boy."; 104 | let new_words = remove_sensitive_word(words); 105 | println!("{}", new_words); 106 | 107 | let new_words = remove_sensitive_word_old(words); 108 | println!("{}", new_words); 109 | } 110 | -------------------------------------------------------------------------------- /构建脚本/hello/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /构建脚本/hello/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "hello-from-generated-code" 7 | version = "0.1.0" 8 | -------------------------------------------------------------------------------- /构建脚本/hello/Cargo.toml: -------------------------------------------------------------------------------- 1 | # workspace = { members = ["链接系统库"] } 2 | [package] 3 | name = "hello-from-generated-code" 4 | version = "0.1.0" 5 | edition = "2024" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | -------------------------------------------------------------------------------- /构建脚本/hello/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::fs; 3 | use std::path::Path; 4 | 5 | // 一些项目希望编译第三方的非 Rust 代码,例如 C 依赖库; 6 | // 一些希望链接本地或者基于源码构建的 C 依赖库;还有一些项目需要功能性的工具, 7 | // 例如在构建之间执行一些代码生成的工作等。 8 | // 对于这些目标,社区已经提供了一些工具来很好的解决,Cargo 并不想替代它们, 9 | // 但是为了给用户带来一些便利,Cargo 提供了自定义构建脚本的方式,来帮助用户更好的解决类似的问题。 10 | 11 | // 若要创建构建脚本,我们只需在项目的根目录下添加一个 build.rs 文件即可。 12 | // 这样一来, Cargo 就会先编译和执行该构建脚本,然后再去构建整个项目。 13 | 14 | // 关于构建脚本的一些使用场景如下: 15 | // 构建 C 依赖库 16 | // 在操作系统中寻找指定的 C 依赖库 17 | // 根据某个说明描述文件生成一个 Rust 模块 18 | // 执行一些平台相关的配置 19 | 20 | // 构建脚本的生命周期 21 | // 在项目被构建之前,Cargo 会将构建脚本编译成一个可执行文件,然后运行该文件并执行相应的任务。 22 | // 在运行的过程中,脚本可以使用之前 println 的方式跟 Cargo 进行通信: 23 | // 通信内容是以 cargo: 开头的格式化字符串。 24 | 25 | // 需要注意的是,Cargo 也不是每次都会重新编译构建脚本, 26 | // 只有当脚本的内容或依赖发生变化时才会。默认情况下, 27 | // 任何文件变化都会触发重新编译,如果你希望对其进行定制,可以使用 rerun-if命令,后文会讲。 28 | 29 | // 在构建脚本成功执行后,我们的项目就会开始进行编译。如果构建脚本的运行过程中发生错误, 30 | // 脚本应该通过返回一个非 0 码来立刻退出,在这种情况下,构建脚本的输出会被打印到终端中。 31 | 32 | // build.rs中可以设置环境变量,这些环境变量会在编译时被 Cargo 传递给 rustc。 33 | fn main() { 34 | let out_dir = env::var_os("OUT_DIR").unwrap(); 35 | let dest_path = Path::new(&out_dir).join("hello.rs"); 36 | fs::write( 37 | &dest_path, 38 | "pub fn message() -> &'static str { 39 | \"Hello, World!\" 40 | } 41 | ", 42 | ) 43 | .unwrap(); 44 | println!("cargo:rerun-if-changed=build.rs"); 45 | 46 | // 打印一条警告信息 47 | println!("cargo:warning=构建脚本正在执行..."); 48 | 49 | // 构建脚本的任何其他标准输出(比如通过println!宏输出的信息)并不会直接显示在终端上, 50 | // 而是被Cargo捕获。只有当构建脚本执行失败(即退出码非零)时, 51 | // 这些输出才会被Cargo显示出来,以帮助诊断问题。 52 | // 如果你希望在构建过程中看到构建脚本的输出(不仅仅是在出错时), 53 | // 你可以在运行cargo build命令时增加--verbose(或-v)参数。 54 | // 使用这个参数,Cargo会输出更多的信息,包括构建脚本的标准输出。例如: 55 | } 56 | 57 | // 以上代码中有几点值得注意: 58 | 59 | // OUT_DIR 环境变量说明了构建脚本的输出目录,也就是最终生成的代码文件的存放地址 60 | // 一般来说,构建脚本不应该修改 OUT_DIR 之外的任何文件 61 | // 这里的代码很简单,但是我们这是为了演示,大家完全可以生成更复杂、更实用的代码 62 | // return-if-changed 指令告诉 Cargo 只有在脚本内容发生变化时,才能重新编译和运行构建脚本。 63 | // 如果没有这一行,项目的任何文件发生变化都会导致 Cargo 重新编译运行该构建脚本 64 | -------------------------------------------------------------------------------- /构建脚本/hello/src/main.rs: -------------------------------------------------------------------------------- 1 | include!(concat!(env!("OUT_DIR"), "/hello.rs")); 2 | // env! 用于在编译时展开为指定的环境变量的值。 3 | // concat! 用于将字符串字面量连接起来成为一个新的&str。 4 | // include!将文件内容插入到这里。 5 | 6 | fn main() { 7 | println!("{}", message()); 8 | env!("OUT_DIR"); 9 | } 10 | 11 | // 这里才是体现真正技术的地方,我们联合使用 rustc 定义的 include! 以及 concat! 和 env! 宏, 12 | // 将生成的代码文件( hello.rs ) 纳入到我们项目的编译流程中。 13 | 14 | // 例子虽然很简单,但是它清晰地告诉了我们该如何生成代码文件以及将这些代码文件纳入到编译中来, 15 | // 大家以后有需要只要回头看看即可。 16 | -------------------------------------------------------------------------------- /构建脚本/条件编译/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /构建脚本/条件编译/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "条件编译" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /构建脚本/条件编译/build.rs: -------------------------------------------------------------------------------- 1 | // 构建脚本可以通过发出 rustc-cfg 指令来开启编译时的条件检查。 2 | // 在本例中,一起来看看 openssl 包是如何支持多版本的 OpenSSL 库的。 3 | 4 | // openssl-sys 包对 OpenSSL 库进行了构建和链接,支持多个不同的实现(例如 LibreSSL )和多个不同的版本。 5 | // 它也使用了 links 配置,这样就可以给其它构建脚本传递所需的信息。 6 | // 例如 version_number ,包含了检测到的 OpenSSL 库的版本号信息。 7 | // openssl-sys 自己的构建脚本中有类似于如下的代码: 8 | // println!("cargo:version_number={:x}", openssl_version); 9 | // 该指令将 version_number 的信息通过环境变量 DEP_OPENSSL_VERSION_NUMBER 的方式 10 | // 传递给直接使用 openssl-sys 的项目。 11 | // 例如 openssl 包提供了更高级的抽象接口,并且它使用了 openssl-sys 作为依赖。 12 | // openssl 的构建脚本会通过环境变量读取 openssl-sys 提供的版本号的信息,然后使用该版本号来生成一些 cfg: 13 | 14 | /* 15 | if let Ok(version) = env::var("DEP_OPENSSL_VERSION_NUMBER") { 16 | let version = u64::from_str_radix(&version, 16).unwrap(); 17 | 18 | if version >= 0x1_00_01_00_0 { 19 | println!("cargo:rustc-cfg=ossl101"); 20 | } 21 | if version >= 0x1_00_02_00_0 { 22 | println!("cargo:rustc-cfg=ossl102"); 23 | } 24 | if version >= 0x1_01_00_00_0 { 25 | println!("cargo:rustc-cfg=ossl110"); 26 | } 27 | if version >= 0x1_01_00_07_0 { 28 | println!("cargo:rustc-cfg=ossl110g"); 29 | } 30 | if version >= 0x1_01_01_00_0 { 31 | println!("cargo:rustc-cfg=ossl111"); 32 | } 33 | } 34 | 35 | */ 36 | 37 | // 这些 cfg 可以跟 cfg 属性 或 cfg 宏一起使用以实现条件编译。 38 | // 例如,在 OpenSSL 1.1 中引入了 SHA3 的支持, 39 | // 那么我们就可以指定只有当版本号为 1.1 时,才包含并编译相关的代码: 40 | 41 | /* 42 | #[cfg(ossl111)] 43 | pub fn sha3_224() -> MessageDigest { 44 | unsafe { MessageDigest(ffi::EVP_sha3_224()) } 45 | } 46 | */ 47 | 48 | // 当然,大家在使用时一定要小心,因为这可能会导致生成的二进制文件进一步依赖当前的构建环境。 49 | // 例如,当二进制可执行文件需要在另一个操作系统中分发运行时,那它依赖的信息对于该操作系统可能是不存在的! 50 | 51 | fn main() {} 52 | -------------------------------------------------------------------------------- /构建脚本/条件编译/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("Hello, world!"); 3 | } 4 | -------------------------------------------------------------------------------- /构建脚本/链接系统库/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /构建脚本/链接系统库/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "libz-sys" 7 | version = "0.1.0" 8 | dependencies = [ 9 | "pkg-config", 10 | ] 11 | 12 | [[package]] 13 | name = "pkg-config" 14 | version = "0.3.29" 15 | source = "registry+https://github.com/rust-lang/crates.io-index" 16 | checksum = "2900ede94e305130c13ddd391e0ab7cbaeb783945ae07a279c268cb05109c6cb" 17 | -------------------------------------------------------------------------------- /构建脚本/链接系统库/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libz-sys" 3 | version = "0.1.0" 4 | edition = "2024" 5 | #这里的 links = "z" 用于告诉 Cargo 我们想要链接到 libz 库, 6 | links = "z" 7 | 8 | [build-dependencies] 9 | pkg-config = "0.3.16" 10 | -------------------------------------------------------------------------------- /构建脚本/链接系统库/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | pkg_config::Config::new().probe("zlib").unwrap(); 3 | println!("cargo:rerun-if-changed=build.rs"); 4 | } 5 | 6 | // 代码很清晰,也很简洁,这里就不再过多介绍, 7 | // 运行 cargo build --vv 来看看部分结果( 系统中需要已经安装 libz 库): 8 | 9 | // [libz-sys 0.1.0] cargo:rustc-link-search=native=/usr/lib 10 | // [libz-sys 0.1.0] cargo:rustc-link-lib=z 11 | // [libz-sys 0.1.0] cargo:rerun-if-changed=build.rs 12 | // 非常棒,pkg-config 帮助我们找到了目标库,并且还告知了 Cargo 所有需要的信息! 13 | 14 | // 实际使用中,我们需要做的比上面的代码更多, 15 | // 例如 libz-sys 包会先检查环境变量 LIBZ_SYS_STATIC 或者 static feature, 16 | // 然后基于源码去构建 libz,而不是直接去使用系统库。 17 | -------------------------------------------------------------------------------- /构建脚本/链接系统库/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::os::raw::{c_uint, c_ulong}; 2 | 3 | extern "C" { 4 | pub fn crc32(crc: c_ulong, buf: *const u8, len: c_uint) -> c_ulong; 5 | } 6 | 7 | #[test] 8 | #[cfg(target_os = "linux")] 9 | fn test_crc32() { 10 | let s = "hello"; 11 | unsafe { 12 | assert_eq!(crc32(0, s.as_ptr(), s.len() as c_uint), 0x3610a686); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /模式匹配/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /模式匹配/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "模式匹配" 7 | version = "0.1.0" 8 | -------------------------------------------------------------------------------- /模式匹配/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "模式匹配" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /流程控制/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /流程控制/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "流程控制" 7 | version = "0.1.0" 8 | -------------------------------------------------------------------------------- /流程控制/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "流程控制" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /猜数游戏/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /猜数游戏/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "cfg-if" 7 | version = "1.0.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 10 | 11 | [[package]] 12 | name = "getrandom" 13 | version = "0.2.9" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" 16 | dependencies = [ 17 | "cfg-if", 18 | "libc", 19 | "wasi", 20 | ] 21 | 22 | [[package]] 23 | name = "libc" 24 | version = "0.2.144" 25 | source = "registry+https://github.com/rust-lang/crates.io-index" 26 | checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" 27 | 28 | [[package]] 29 | name = "ppv-lite86" 30 | version = "0.2.17" 31 | source = "registry+https://github.com/rust-lang/crates.io-index" 32 | checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" 33 | 34 | [[package]] 35 | name = "rand" 36 | version = "0.8.5" 37 | source = "registry+https://github.com/rust-lang/crates.io-index" 38 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 39 | dependencies = [ 40 | "libc", 41 | "rand_chacha", 42 | "rand_core", 43 | ] 44 | 45 | [[package]] 46 | name = "rand_chacha" 47 | version = "0.3.1" 48 | source = "registry+https://github.com/rust-lang/crates.io-index" 49 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 50 | dependencies = [ 51 | "ppv-lite86", 52 | "rand_core", 53 | ] 54 | 55 | [[package]] 56 | name = "rand_core" 57 | version = "0.6.4" 58 | source = "registry+https://github.com/rust-lang/crates.io-index" 59 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 60 | dependencies = [ 61 | "getrandom", 62 | ] 63 | 64 | [[package]] 65 | name = "wasi" 66 | version = "0.11.0+wasi-snapshot-preview1" 67 | source = "registry+https://github.com/rust-lang/crates.io-index" 68 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 69 | 70 | [[package]] 71 | name = "猜数游戏" 72 | version = "0.1.0" 73 | dependencies = [ 74 | "rand", 75 | ] 76 | -------------------------------------------------------------------------------- /猜数游戏/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "猜数游戏" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | rand = "0.8.5" 10 | -------------------------------------------------------------------------------- /猜数游戏/src/main.rs: -------------------------------------------------------------------------------- 1 | use rand::Rng; 2 | use std::io::Write; //io::stdout().flush() 是Write trait的一个方法,所以需要导入 3 | fn main() { 4 | println!("猜数游戏!!!"); 5 | let secret_number = rand::thread_rng().gen_range(1..=100); 6 | // println!("秘密数字是:{}", secret_number); 7 | let mut guess = String::new(); 8 | 9 | loop { 10 | print!("猜一个数:"); 11 | // 默认情况下stdout通常是行缓冲的,因此可能需要使用io::stdout().flush()以确保输出到达终端。 12 | std::io::stdout().flush().unwrap(); // 刷新缓冲区 13 | 14 | guess.clear(); 15 | std::io::stdin().read_line(&mut guess).expect("读取行失败"); 16 | // parse 返回一个 Result 类型,它是一个枚举,它的成员是 Ok 或 Err。 17 | let guess: i32 = match guess.trim().parse() { 18 | Ok(num) => num, 19 | Err(e) => { 20 | println!("{} 不是一个数字: {}", guess.trim(), e); 21 | continue; 22 | } 23 | }; 24 | println!("你猜的数是:{} ", guess); 25 | match guess.cmp(&secret_number) { 26 | std::cmp::Ordering::Less => println!("Too small!"), 27 | std::cmp::Ordering::Greater => println!("Too big!"), 28 | std::cmp::Ordering::Equal => { 29 | println!("\x1b[1;32mYou win!\x1b[0m"); 30 | break; 31 | } 32 | } 33 | println!(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /生命周期/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /生命周期/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "生命周期" 7 | version = "0.1.0" 8 | -------------------------------------------------------------------------------- /生命周期/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "生命周期" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /生命周期/src/advance.rs: -------------------------------------------------------------------------------- 1 | pub fn stuidy_lifetime_advance() { 2 | println!("-----------------生命周期进阶(复杂的例子)-----------------"); 3 | let mut list = List { 4 | manager: Manager { text: "hello" }, 5 | }; 6 | 7 | list.get_interface().noop(); 8 | 9 | println!("Interface should be dropped here and the borrow released"); 10 | 11 | use_list(&list); 12 | 13 | println!( 14 | "------------------高阶特征约束 高阶生命周期约束(Higher-ranked trait bounds)-----------------" 15 | ); 16 | 17 | higher_ranked_lifetime_bounds(); 18 | } 19 | 20 | fn higher_ranked_lifetime_bounds() { 21 | /* 22 | fn call_on_ref_zero<'a, F>(f: F) 23 | where 24 | F: Fn(&'a i32), 25 | { 26 | let zero = 0; 27 | f(&zero); //`zero` does not live long enough 28 | } 29 | */ 30 | // 我们想要f函数能够处理任何生命周期的引用 31 | fn call_on_ref_zero(f: F) 32 | where 33 | F: for<'a> Fn(&'a i32), 34 | { 35 | let zero = 0; 36 | f(&zero); 37 | } 38 | // 通过使用 for<'a>,我们告诉Rust编译器:"这个函数 F 可以处理任何生命周期的引用,包括非常短暂的生命周期"。 39 | // 这允许我们在函数内部创建一个临时值(zero),取它的引用,并将这个短生命周期的引用传递给 F,而不会有任何生命周期冲突。 40 | 41 | call_on_ref_zero(|a: &i32| println!("{:?}", a)); 42 | 43 | // 实例: 44 | // for<'de> Deserialize<'de> 是一个高阶生命周期约束 (Higher-Rank Trait Bound, HRTB)。 45 | // 它表示:“对于 任何 生命周期 'de,T 都能实现 Deserialize<'de>”。 46 | // 这正是 serde 反序列化函数所期望的——它能从任何生命周期的输入数据中反序列化出 T。 47 | } 48 | 49 | // 生命周期存在的意义是保证不会出现空引用(悬垂引用), 50 | // 即引用对象的生命周期至少要比当前结构体的生命周期长(可以是相同的)。 51 | // manager为持有引用的变量,生命周期的标注是对于引用来说的,而不是变量。 52 | struct Interface<'a, 'b: 'a> { 53 | _manager: &'a mut Manager<'b>, 54 | } 55 | 56 | impl<'a, 'b> Interface<'a, 'b> { 57 | pub fn noop(self) { 58 | println!("interface consumed"); 59 | } 60 | } 61 | 62 | struct Manager<'a> { 63 | text: &'a str, 64 | } 65 | 66 | struct List<'a> { 67 | manager: Manager<'a>, 68 | } 69 | 70 | impl<'a> List<'a> { 71 | // pub fn get_interface(&'a mut self) -> Interface { 72 | // 注释中方法的生命周期标注是有问题的, 73 | // 该方法的参数的生命周期是 'a,而 List 的生命周期也是 'a,说明该方法至少活得跟 List 一样久, 74 | // 而实际上这个可变引用在方法执行完毕后就会被释放,所以应该把 'a 的生命周期标注改成 'b, 75 | // 代表这个方法中可变引用的生命周期至少为 'b , 'b 是 'a 的子生命周期,即 'b <= 'a 。 76 | pub fn get_interface<'b>(&'b mut self) -> Interface<'b, 'a> { 77 | Interface { 78 | _manager: &mut self.manager, 79 | } 80 | } 81 | } 82 | 83 | fn use_list(list: &List) { 84 | println!("{}", list.manager.text); 85 | } 86 | -------------------------------------------------------------------------------- /配置文件/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | *.json 3 | -------------------------------------------------------------------------------- /配置文件/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "配置文件" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | serde = { version = "1.0", features = ["derive"] } 10 | serde_json = "1.0" 11 | serde_yaml = "0.9" 12 | ini = "1" 13 | config = "0.13" 14 | toml = "0.8" 15 | 16 | [[example]] 17 | name = "example1" 18 | path = "examples/*" 19 | -------------------------------------------------------------------------------- /配置文件/examples/main1.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | // https://serde.rs/examples.html 4 | 5 | #[derive(Serialize, Deserialize, Debug)] 6 | struct Foo<'a> { 7 | a: u8, 8 | b: i32, 9 | s: String, 10 | s1: &'a str, 11 | f: f64, 12 | // 忽略字段 13 | #[serde(skip)] 14 | ii: String, 15 | 16 | foo: Bar, 17 | 18 | #[serde(flatten)] 19 | // 效果1: "A": 7 20 | // 效果2: "my_enum": { "A": 7 } 21 | my_enum: MyEnum, 22 | my_enum2: MyEnum, 23 | my_enum3: MyEnum, 24 | } 25 | 26 | #[derive(Serialize, Deserialize, Debug)] 27 | struct Bar { 28 | bar_a: u8, 29 | bar_b: i32, 30 | bar_s: String, 31 | bar_f: f64, 32 | } 33 | 34 | #[derive(Serialize, Deserialize, Debug)] 35 | enum MyEnum { 36 | A(u8), 37 | #[serde(rename = "BBB")] 38 | B(Vec), 39 | C, 40 | #[serde(alias = "DDD")] 41 | // 添加别名,反序列化时,可以使用别名 42 | D(u128), 43 | } 44 | 45 | fn main() { 46 | let foo = Foo { 47 | a: 1, 48 | b: 2, 49 | s: "hello".to_string(), 50 | s1: "world", 51 | f: 3.14, 52 | ii: "ignore".to_string(), 53 | foo: Bar { 54 | bar_a: 4, 55 | bar_b: 5, 56 | bar_s: "bar".to_string(), 57 | bar_f: 6.28, 58 | }, 59 | my_enum: MyEnum::A(7), 60 | my_enum2: MyEnum::C, 61 | my_enum3: MyEnum::D(8), 62 | }; 63 | println!("field ii:{:?}", foo.ii); 64 | 65 | let json = serde_json::to_string(&foo).unwrap(); 66 | println!("序列化:{}", json); 67 | 68 | let foo1: Foo = serde_json::from_str(&json).unwrap(); 69 | println!("反序列化:{:?}", foo1); 70 | 71 | let my_enum = MyEnum::B(vec!["hello".to_string(), "world".to_string()]); 72 | let json = serde_json::to_string(&my_enum).unwrap(); 73 | println!("序列化:{}", json); 74 | 75 | let my_enum1: MyEnum = serde_json::from_str(&json).unwrap(); 76 | println!("反序列化:{:?}", my_enum1); 77 | 78 | let my_enum = MyEnum::D(8); 79 | let json = serde_json::to_string(&my_enum).unwrap(); 80 | println!("序列化:{}", json); 81 | 82 | let json_str = r#"{"DDD":8}"#; 83 | let my_enum1: MyEnum = serde_json::from_str(&json_str).unwrap(); 84 | println!("反序列化:{:?}", my_enum1); 85 | } 86 | -------------------------------------------------------------------------------- /配置文件/examples/main2.rs: -------------------------------------------------------------------------------- 1 | use serde::ser::Error; 2 | use serde::{Serialize, Serializer}; 3 | 4 | #[derive(Debug)] 5 | pub enum Gender { 6 | Female, // 雌性 7 | Male, // 雄性 8 | Neither, // 无性 9 | Both, // 中性 10 | Trans { from: Box, to: Box }, // 跨性别 11 | } 12 | 13 | // 自定义序列化实现。 14 | // https://www.luogu.com.cn/blog/HoshinoTented/serde-in-rust 15 | impl Serialize for Gender { 16 | fn serialize(&self, serializer: S) -> Result<::Ok, ::Error> 17 | where 18 | S: Serializer, 19 | { 20 | let str = match self { 21 | // 根据枚举值来决定字符串内容 22 | Gender::Female => "female", 23 | Gender::Male => "male", 24 | Gender::Neither => "none", 25 | Gender::Both => "both", 26 | Gender::Trans { from, to } => { 27 | let pair = (from.as_ref(), to.as_ref()); 28 | 29 | match pair { 30 | (Gender::Female, Gender::Male) => "ftm", 31 | (Gender::Male, Gender::Female) => "mtf", 32 | 33 | _ => Err(::Error::custom( 34 | "Trans variant only supports mtf and ftm", 35 | ))?, // 为了简单这里只支持双性的跨性别 36 | } 37 | } 38 | }; 39 | 40 | serializer.serialize_str(str) // 序列化一个字符串 41 | } 42 | } 43 | 44 | #[derive(Debug, Serialize)] // 注意删去 Deserialize,因为我们还没为 Gender 提供反序列化 45 | struct Person { 46 | name: String, 47 | age: i32, 48 | gender: Gender, 49 | } 50 | 51 | fn main() { 52 | let hoshino = Person { 53 | name: String::from("hoshino"), 54 | age: 4, 55 | gender: Gender::Trans { 56 | from: Gender::Male.into(), 57 | to: Gender::Female.into(), 58 | }, 59 | }; 60 | 61 | let json = serde_json::to_string(&hoshino).unwrap(); // 使用 serde_json 库来进行 JSON 的序列化 62 | println!("{}", json); 63 | } 64 | -------------------------------------------------------------------------------- /配置文件/examples/main3.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("Hello, world!"); 3 | } 4 | -------------------------------------------------------------------------------- /配置文件/examples/main4.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("Hello, world!"); 3 | } 4 | -------------------------------------------------------------------------------- /配置文件/examples/main5.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("Hello, world!"); 3 | } 4 | -------------------------------------------------------------------------------- /配置文件/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("Hello, world!"); 3 | } 4 | -------------------------------------------------------------------------------- /错误处理与格式化输出/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /错误处理与格式化输出/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "anyhow" 7 | version = "1.0.75" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" 10 | 11 | [[package]] 12 | name = "proc-macro2" 13 | version = "1.0.67" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" 16 | dependencies = [ 17 | "unicode-ident", 18 | ] 19 | 20 | [[package]] 21 | name = "quote" 22 | version = "1.0.33" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" 25 | dependencies = [ 26 | "proc-macro2", 27 | ] 28 | 29 | [[package]] 30 | name = "sayhello_lib" 31 | version = "0.1.0" 32 | 33 | [[package]] 34 | name = "syn" 35 | version = "2.0.37" 36 | source = "registry+https://github.com/rust-lang/crates.io-index" 37 | checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8" 38 | dependencies = [ 39 | "proc-macro2", 40 | "quote", 41 | "unicode-ident", 42 | ] 43 | 44 | [[package]] 45 | name = "thiserror" 46 | version = "1.0.49" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "1177e8c6d7ede7afde3585fd2513e611227efd6481bd78d2e82ba1ce16557ed4" 49 | dependencies = [ 50 | "thiserror-impl", 51 | ] 52 | 53 | [[package]] 54 | name = "thiserror-impl" 55 | version = "1.0.49" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc" 58 | dependencies = [ 59 | "proc-macro2", 60 | "quote", 61 | "syn", 62 | ] 63 | 64 | [[package]] 65 | name = "unicode-ident" 66 | version = "1.0.12" 67 | source = "registry+https://github.com/rust-lang/crates.io-index" 68 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 69 | 70 | [[package]] 71 | name = "错误处理与格式化输出" 72 | version = "0.1.0" 73 | dependencies = [ 74 | "anyhow", 75 | "sayhello_lib", 76 | "thiserror", 77 | ] 78 | -------------------------------------------------------------------------------- /错误处理与格式化输出/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "错误处理与格式化输出" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | sayhello_lib = { path = "E:/Doraemon/IT/Rust/study/sayhello_lib" } 10 | thiserror = "1.0" 11 | anyhow = "1.0" 12 | sqlx = "0.8" 13 | -------------------------------------------------------------------------------- /错误处理与格式化输出/hello.txt: -------------------------------------------------------------------------------- 1 | hello world -------------------------------------------------------------------------------- /错误处理与格式化输出/hello2.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/doraemonkeys/rust_code_snippets/9a0b29d113cf59733fc1580997c9008cb822ce87/错误处理与格式化输出/hello2.txt -------------------------------------------------------------------------------- /错误处理与格式化输出/src/format.rs: -------------------------------------------------------------------------------- 1 | pub fn study_format_output() { 2 | println!("------------------study_format_output------------------"); 3 | // 位置参数 4 | println!("----------------- 位置参数 -----------------"); 5 | println!("{}{}", 1, 2); // =>"12" 6 | println!("{1}{0}", 1, 2); // =>"21" 7 | 8 | // => Alice, this is Bob. Bob, this is Alice 9 | println!("{0}, this is {1}. {1}, this is {0}", "Alice", "Bob"); 10 | println!("{1}{}{0}{}", 1, 2); // => 2112 11 | 12 | // 具名参数 13 | // 带名称的参数必须放在不带名称参数的后面。 14 | println!("----------------- 具名参数 -----------------"); 15 | println!("{argument}", argument = "test"); // => "test" 16 | println!("{name} {}", 1, name = 2); // => "2 1" 17 | println!("{a} {c} {b}", a = "a", b = 'b', c = 3); // => "a 3 b" 18 | 19 | // 格式化参数 20 | println!("----------------- 格式化参数 -----------------"); 21 | let v = 3.1415926; 22 | // Display => 3.14 23 | println!("{:.2}", v); 24 | // Debug => 3.14 25 | println!("{:.2?}", v); 26 | 27 | // 字符串填充 28 | println!("----------------- 字符串填充 -----------------"); 29 | //----------------------------------- 30 | // 以下全部输出 "Hello x !" 31 | // 为"x"后面填充空格,补齐宽度5 32 | println!("Hello {:5}!", "x"); 33 | // 使用参数5来指定宽度 34 | println!("Hello {:1$}!", "x", 5); 35 | // 使用x作为占位符输出内容,同时使用5作为宽度 36 | println!("Hello {1:0$}!", 5, "x"); 37 | // 使用有名称的参数作为宽度 38 | println!("Hello {:width$}!", "x", width = 5); 39 | //----------------------------------- 40 | 41 | // 使用参数5为参数x指定宽度,同时在结尾输出参数5 => Hello x !5 42 | println!("Hello {:1$}!{}", "x", 5); 43 | 44 | // 数字填充:符号和 0 45 | println!("----------------- 数字填充:符号和 0 -----------------"); 46 | // 宽度是5 => Hello 5! 47 | println!("Hello {:5}!", 5); 48 | // 显式的输出正号 => Hello +5! 49 | println!("Hello {:+}!", 5); 50 | // 宽度5,使用0进行填充 => Hello 00005! 51 | println!("Hello {:05}!", 5); 52 | // 负号也要占用一位宽度 => Hello -0005! 53 | println!("Hello {:05}!", -5); 54 | 55 | // 对齐 56 | println!("----------------- 对齐 -----------------"); 57 | // 以下全部都会补齐5个字符的长度 58 | // 左对齐 => Hello x ! 59 | println!("Hello {:<5}!", "x"); 60 | // 右对齐 => Hello x! 61 | println!("Hello {:>5}!", "x"); 62 | // 居中对齐 => Hello x ! 63 | println!("Hello {:^5}!", "x"); 64 | 65 | // 对齐并使用指定符号填充 => Hello x&&&&! 66 | // 指定符号填充的前提条件是必须有对齐字符 67 | println!("Hello {:&<5}!", "x"); 68 | 69 | // 精度 70 | // 精度可以用于控制浮点数的精度或者字符串的长度 71 | println!("----------------- 精度 -----------------"); 72 | let v = 3.1415926; 73 | // 保留小数点后两位 => 3.14 74 | println!("{:.2}", v); 75 | // 带符号保留小数点后两位 => +3.14 76 | println!("{:+.2}", v); 77 | // 不带小数 => 3 78 | println!("{:.0}", v); 79 | // 通过参数来设定精度 => 3.1416,相当于{:.4} 80 | println!("{:.1$}", v, 4); 81 | 82 | let s = "hi我是Sunface孙飞"; 83 | // 保留字符串前三个字符 => hi我 84 | println!("{:.3}", s); 85 | // {:.*}接收两个参数,第一个是精度,第二个是被格式化的值 => Hello abc! 86 | println!("Hello {:.*}!", 3, "abcdefg"); 87 | 88 | // 进制 89 | println!("----------------- 进制 -----------------"); 90 | // 二进制 => 0b11011! 91 | println!("{:#b}!", 27); 92 | // 八进制 => 0o33! 93 | println!("{:#o}!", 27); 94 | // 十进制 => 27! 95 | println!("{}!", 27); 96 | // 小写十六进制 => 0x1b! 97 | println!("{:#x}!", 27); 98 | // 大写十六进制 => 0x1B! 99 | println!("{:#X}!", 27); 100 | 101 | // 不带前缀的十六进制 => 1b! 102 | println!("{:x}!", 27); 103 | 104 | // 使用0填充二进制,宽度为10 => 0b00011011! 105 | println!("{:#010b}!", 27); 106 | 107 | // 指数 108 | println!("----------------- 指数 -----------------"); 109 | println!("{:2e}", 1000000000); // => 1e9 110 | println!("{:2E}", 1000000000); // => 1E9 111 | 112 | // 指针地址 113 | let v = vec![1, 2, 3]; 114 | println!("{:p}", v.as_ptr()); // => 0x600002324050 115 | let s = "hi我是Sunface孙飞"; 116 | println!("{:p}", s.as_ptr()); // => 0x600002324050 117 | 118 | // 在格式化字符串时捕获环境中的值 119 | let person = get_person(); 120 | println!("Hello, {person}!"); 121 | } 122 | 123 | fn get_person() -> String { 124 | String::from("sunface") 125 | } 126 | --------------------------------------------------------------------------------