├── .github └── workflows │ └── build.yml ├── .gitignore ├── .pre-commit-config.yaml ├── 01_stack_heap ├── Cargo.toml ├── misc │ └── varargs.c └── src │ ├── error.rs │ ├── pointer.rs │ └── string.rs ├── 02_concepts ├── Cargo.toml ├── misc │ ├── crash.c │ ├── type.c │ └── type.py └── src │ └── shape.rs ├── 03_grammar ├── .gitignore ├── concepts │ ├── Cargo.toml │ └── src │ │ ├── event.rs │ │ ├── fib.rs │ │ └── lib.rs ├── scrape_url │ ├── .gitignore │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── scrape_url_args │ ├── .gitignore │ ├── Cargo.toml │ └── src │ │ └── main.rs └── scrape_url_with_error_handling │ ├── .gitignore │ ├── Cargo.toml │ └── src │ └── main.rs ├── 04_httpie ├── Cargo.toml ├── examples │ ├── cli.rs │ ├── cli_get.rs │ └── cli_verify.rs └── src │ └── main.rs ├── 05_thumbor ├── Cargo.toml ├── abi.proto ├── build.rs ├── rust-logo.png └── src │ ├── engine │ ├── mod.rs │ └── photon.rs │ ├── imageproc.rs │ ├── main.rs │ ├── pb │ ├── abi.rs │ └── mod.rs │ ├── server1.rs │ └── server2.rs ├── 06_queryer ├── Cargo.toml.bak ├── README.md ├── data-viewer │ ├── .gitignore │ ├── .vscode │ │ └── settings.json │ ├── dist │ │ └── index.html │ ├── package-lock.json │ ├── package.json │ ├── src-tauri │ │ ├── .gitignore │ │ ├── Cargo.toml │ │ ├── icons │ │ │ ├── 128x128.png │ │ │ ├── 128x128@2x.png │ │ │ ├── 32x32.png │ │ │ ├── Square107x107Logo.png │ │ │ ├── Square142x142Logo.png │ │ │ ├── Square150x150Logo.png │ │ │ ├── Square284x284Logo.png │ │ │ ├── Square30x30Logo.png │ │ │ ├── Square310x310Logo.png │ │ │ ├── Square44x44Logo.png │ │ │ ├── Square71x71Logo.png │ │ │ ├── Square89x89Logo.png │ │ │ ├── StoreLogo.png │ │ │ ├── icon.icns │ │ │ ├── icon.ico │ │ │ └── icon.png │ │ ├── rustfmt.toml │ │ ├── src │ │ │ ├── build.rs │ │ │ └── main.rs │ │ └── tauri.conf.json │ └── yarn.lock ├── queryer-js │ ├── .gitignore │ ├── Cargo.toml │ ├── README.md │ ├── package.json │ ├── src │ │ └── lib.rs │ └── yarn.lock ├── queryer-py │ ├── Cargo.toml │ ├── README.md │ ├── build.rs │ └── src │ │ └── lib.rs └── queryer │ ├── Cargo.toml │ ├── examples │ ├── covid.rs │ └── dialect.rs │ └── src │ ├── convert.rs │ ├── dialect.rs │ ├── fetcher.rs │ ├── lib.rs │ └── loader.rs ├── 07_ownership ├── Cargo.toml └── src │ ├── check_copy.rs │ ├── ownership1.rs │ └── ownership_error.rs ├── 08_borrow ├── Cargo.toml ├── misc │ ├── multiple_mut_ref.py │ └── reference.c └── src │ ├── heap_reference_stack.rs │ ├── heap_reference_stack_outlive.rs │ ├── local_ref.rs │ ├── manual_drop.rs │ ├── multiple_mut_ref.rs │ ├── multiple_mut_ref1.rs │ ├── ownership_reference.rs │ ├── reborrow.rs │ ├── reborrow1.rs │ └── vec_grow.rs ├── 09_multi_owner ├── Cargo.toml └── src │ ├── dag.rs │ ├── dag_mut.rs │ └── refcell.rs ├── 10_lifetime ├── Cargo.toml └── src │ ├── lifetime.rs │ ├── lifetime1.rs │ ├── lifetime2.rs │ ├── lifetime3.rs │ ├── lifetime4.rs │ ├── lifetime_mono.rs │ ├── lifetime_non_lexical.rs │ └── strtok.rs ├── 11_memory ├── Cargo.toml ├── examples │ ├── alignment.rs │ ├── raii.rs │ └── size.rs ├── misc │ └── test.c └── src │ └── lib.rs ├── 12_type_system ├── Cargo.toml └── src │ ├── constant.rs │ ├── id.rs │ ├── reader.rs │ └── writer.rs ├── 13_traits ├── Cargo.toml └── src │ ├── animal.rs │ ├── complex.rs │ ├── dd.rs │ ├── formatter.rs │ ├── iterator.rs │ ├── parse.rs │ ├── parse1.rs │ ├── parse2.rs │ ├── trait_object.rs │ ├── trait_object_internal.rs │ └── write.rs ├── 14_sys_traits ├── .vscode │ └── settings.json ├── Cargo.toml └── src │ ├── convert.rs │ ├── copy_drop.rs │ ├── default.rs │ ├── deref.rs │ ├── dev.rs │ ├── linked_list.rs │ ├── send_sync.rs │ └── sized.rs ├── 15_smart_pointers ├── Cargo.toml └── src │ ├── allocator.rs │ ├── borrow.rs │ ├── box.rs │ ├── cow1.rs │ ├── cow2.rs │ ├── guard.rs │ ├── guard1.rs │ └── mystring.rs ├── 16_data_structure ├── Cargo.toml └── src │ ├── boxed.rs │ ├── iter.rs │ ├── iter_ext.rs │ ├── slice1.rs │ ├── slice2.rs │ ├── slice3.rs │ ├── slice4.rs │ ├── slice5.rs │ └── type_name.rs ├── 17_hash_table ├── Cargo.toml ├── hashmap2.txt └── src │ ├── btreemap1.rs │ ├── hash.rs │ ├── hashmap1.rs │ ├── hashmap2.rs │ ├── hashmap3.rs │ └── siphasher.rs ├── 18_error_handling └── error_handling.go ├── 19_closure ├── Cargo.toml ├── closure.txt ├── closure1.txt ├── closure2.txt ├── closure3.txt ├── examples │ ├── callable.rs │ ├── callable_result.rs │ ├── closure_size.rs │ ├── closure_size1.rs │ ├── fn1.rs │ ├── fn_mut1.rs │ ├── fn_once1.rs │ ├── fn_once2.rs │ ├── fn_return.rs │ ├── mimic_closure.rs │ └── thread.rs ├── misc │ └── closure.go └── src │ └── lib.rs ├── 21_kv ├── Cargo.toml ├── abi.proto ├── build.rs ├── examples │ ├── client.rs │ ├── dummy_server.rs │ └── server.rs └── src │ ├── error.rs │ ├── lib.rs │ ├── pb │ ├── abi.rs │ └── mod.rs │ ├── service │ ├── command_service.rs │ └── mod.rs │ └── storage │ ├── memory.rs │ └── mod.rs ├── 23_advanced_generics ├── Cargo.toml ├── misc │ └── phantom.ts └── src │ ├── auth.rs │ ├── complex_args.rs │ ├── functions.rs │ ├── identifier.rs │ ├── iterator.rs │ └── lib.rs ├── 24_advanced_trait_objects ├── Cargo.toml ├── benches │ └── trait_object.rs └── src │ ├── lib.rs │ ├── service.rs │ └── trait_object_in_fn.rs ├── 26_kv ├── Cargo.toml ├── abi.proto ├── build.rs ├── examples │ ├── client.rs │ ├── dummy_server.rs │ ├── server.rs │ └── server_with_sled.rs └── src │ ├── error.rs │ ├── lib.rs │ ├── pb │ ├── abi.rs │ └── mod.rs │ ├── service │ ├── command_service.rs │ └── mod.rs │ └── storage │ ├── memory.rs │ ├── mod.rs │ └── sleddb.rs ├── 29_network ├── Cargo.toml ├── examples │ ├── async_client.rs │ ├── async_listener.rs │ ├── client.rs │ ├── framed_client.rs │ ├── framed_server.rs │ ├── listener.rs │ ├── p2p_chat.rs │ └── rocket_server.rs └── src │ └── lib.rs ├── 30_unsafe ├── Cargo.toml ├── examples │ ├── c_ffi.rs │ ├── raw_pointer.rs │ ├── raw_pointer_crash.rs │ ├── rc_send.rs │ ├── split.rs │ ├── static_mutable.rs │ ├── static_mutable_safe.rs │ ├── static_mutable_safe1.rs │ ├── unsafe_fn1.rs │ ├── unsafe_fn2.rs │ └── unsafe_trait.rs └── src │ └── lib.rs ├── 31_ffi ├── bzlib_sys │ ├── Cargo.toml │ ├── README.md │ ├── build.rs │ ├── src │ │ ├── bindings.rs │ │ └── lib.rs │ └── wrapper.h ├── ffi │ ├── Cargo.toml │ ├── bindings.h │ ├── build.rs │ └── src │ │ └── lib.rs └── ffi_math │ ├── ..swiftdoc │ ├── ..swiftsourceinfo │ ├── Cargo.toml │ ├── build.rs │ ├── ffi_math.swiftmodule │ ├── libffi_math.dylib │ └── src │ ├── lib.rs │ ├── math.swift │ ├── math.udl │ ├── mathFFI.h │ ├── mathFFI.modulemap │ └── uniffi │ └── math │ └── math.kt ├── 32_xunmi_py ├── .gitignore ├── Cargo.toml ├── Makefile ├── README.md ├── build.rs ├── fixtures │ ├── config.yml │ ├── test.yml │ └── wiki_00.xml ├── index_wiki.ipynb ├── src │ └── lib.rs ├── xunmi-py │ ├── .gitignore │ ├── Cargo.toml │ ├── Makefile │ ├── README.md │ ├── build.rs │ ├── fixtures │ │ ├── config.yml │ │ ├── test.yml │ │ └── wiki_00.xml │ ├── index_wiki.ipynb │ ├── src │ │ └── lib.rs │ └── xunmi │ │ └── __init__.py └── xunmi │ └── __init__.py ├── 33_concurrency ├── Cargo.toml ├── examples │ ├── actor.rs │ ├── condvar.rs │ ├── metrics.rs │ ├── naive_lock.rs │ └── naive_lock_with_atomic.rs └── src │ └── lib.rs ├── 35_con_utils ├── Cargo.toml └── src │ ├── channel.rs │ └── lib.rs ├── 36_kv ├── Cargo.toml ├── abi.proto ├── build.rs ├── examples │ ├── client.rs │ ├── dummy_server.rs │ ├── server.rs │ ├── server_with_codec.rs │ └── server_with_sled.rs └── src │ ├── client.rs │ ├── error.rs │ ├── lib.rs │ ├── network │ ├── frame.rs │ └── mod.rs │ ├── pb │ ├── abi.rs │ └── mod.rs │ ├── server.rs │ ├── service │ ├── command_service.rs │ └── mod.rs │ └── storage │ ├── memory.rs │ ├── mod.rs │ └── sleddb.rs ├── 37_kv ├── .gitignore ├── Cargo.toml ├── abi.proto ├── build.rs ├── examples │ ├── client.rs │ ├── dummy_server.rs │ ├── gen_cert.rs │ ├── server.rs │ ├── server_with_codec.rs │ └── server_with_sled.rs ├── fixtures │ ├── .gitkeep │ ├── ca.cert │ ├── ca.key │ ├── client.cert │ ├── client.key │ ├── server.cert │ └── server.key └── src │ ├── client.rs │ ├── error.rs │ ├── lib.rs │ ├── network │ ├── frame.rs │ ├── mod.rs │ └── tls.rs │ ├── pb │ ├── abi.rs │ └── mod.rs │ ├── server.rs │ ├── service │ ├── command_service.rs │ └── mod.rs │ └── storage │ ├── memory.rs │ ├── mod.rs │ └── sleddb.rs ├── 38_async ├── Cargo.toml ├── examples │ ├── async_io.rs │ ├── async_test.rs │ ├── async_thread.rs │ ├── blocking_async_task.rs │ ├── mutex.rs │ ├── pow.rs │ ├── sync_io.rs │ ├── tcp_listener.rs │ └── thread_io.rs └── src │ └── lib.rs ├── 39_async_internals ├── Cargo.toml ├── examples │ ├── async_internals.rs │ ├── async_type.rs │ ├── self_ref.rs │ ├── self_ref1.rs │ └── self_ref_pinned.rs └── src │ └── lib.rs ├── 40_async_stream ├── Cargo.toml ├── examples │ ├── async_read.rs │ ├── fibonacci.rs │ ├── readline.rs │ ├── sink.rs │ ├── sink_file.rs │ ├── stream_iter.rs │ ├── yamux_client.rs │ └── yamux_server.rs └── src │ └── lib.rs ├── 41_kv ├── .gitignore ├── Cargo.toml ├── abi.proto ├── build.rs ├── examples │ ├── client.rs │ ├── dummy_server.rs │ ├── gen_cert.rs │ ├── server.rs │ ├── server_with_codec.rs │ └── server_with_sled.rs ├── fixtures │ ├── .gitkeep │ ├── ca.cert │ ├── ca.key │ ├── client.cert │ ├── client.key │ ├── server.cert │ └── server.key └── src │ ├── client.rs │ ├── error.rs │ ├── lib.rs │ ├── network │ ├── frame.rs │ ├── mod.rs │ ├── stream.rs │ └── tls.rs │ ├── pb │ ├── abi.rs │ └── mod.rs │ ├── server.rs │ ├── service │ ├── command_service.rs │ └── mod.rs │ └── storage │ ├── memory.rs │ ├── mod.rs │ └── sleddb.rs ├── 42_kv ├── .gitignore ├── Cargo.toml ├── abi.proto ├── build.rs ├── diff_client ├── diff_refactor ├── diff_service ├── diff_topic ├── diff_yamux ├── examples │ ├── client.rs │ ├── dummy_server.rs │ ├── gen_cert.rs │ ├── server.rs │ ├── server_with_codec.rs │ └── server_with_sled.rs ├── fixtures │ ├── .gitkeep │ ├── ca.cert │ ├── ca.key │ ├── client.cert │ ├── client.key │ ├── server.cert │ └── server.key └── src │ ├── client.rs │ ├── error.rs │ ├── lib.rs │ ├── network │ ├── frame.rs │ ├── mod.rs │ ├── multiplex.rs │ ├── stream.rs │ ├── stream_result.rs │ └── tls.rs │ ├── pb │ ├── abi.rs │ └── mod.rs │ ├── server.rs │ ├── service │ ├── command_service.rs │ ├── mod.rs │ ├── topic.rs │ └── topic_service.rs │ └── storage │ ├── memory.rs │ ├── mod.rs │ └── sleddb.rs ├── 43_docdoc ├── Cargo.toml └── src │ └── lib.rs ├── 44_data_processing ├── Cargo.toml ├── data │ ├── .gitignore │ ├── create_users.sql │ └── example.db ├── examples │ ├── log.rs │ └── user.rs ├── fixtures │ ├── analyze.sql │ ├── log_schema.yml │ ├── logs.csv │ └── nginx_logs.csv └── src │ └── lib.rs ├── 45_arch ├── Cargo.toml ├── examples │ └── pipeline.rs └── src │ └── lib.rs ├── 46_kv ├── .gitignore ├── Cargo.toml ├── abi.proto ├── benches │ └── pubsub.rs ├── build.rs ├── diff_benchmark ├── diff_config ├── diff_logging ├── diff_telemetry ├── examples │ ├── client.rs │ ├── dummy_server.rs │ ├── gen_cert.rs │ ├── gen_config.rs │ ├── server.rs │ ├── server_with_codec.rs │ └── server_with_sled.rs ├── fixtures │ ├── .gitkeep │ ├── ca.cert │ ├── ca.key │ ├── client.cert │ ├── client.conf │ ├── client.key │ ├── server.cert │ ├── server.conf │ └── server.key ├── src │ ├── client.rs │ ├── config.rs │ ├── error.rs │ ├── lib.rs │ ├── network │ │ ├── frame.rs │ │ ├── mod.rs │ │ ├── multiplex.rs │ │ ├── stream.rs │ │ ├── stream_result.rs │ │ └── tls.rs │ ├── pb │ │ ├── abi.rs │ │ └── mod.rs │ ├── server.rs │ ├── service │ │ ├── command_service.rs │ │ ├── mod.rs │ │ ├── topic.rs │ │ └── topic_service.rs │ └── storage │ │ ├── memory.rs │ │ ├── mod.rs │ │ └── sleddb.rs └── tests │ └── server.rs ├── 47_48_macros ├── Cargo.toml ├── README.md ├── examples │ ├── command.rs │ ├── command_with_attr.rs │ ├── query.rs │ └── rule.rs ├── src │ ├── builder.rs │ ├── builder_with_attr.rs │ ├── lib.rs │ └── raw_builder.rs └── templates │ └── builder.j2 ├── Cargo.toml ├── LICENSE ├── README.md ├── deny.toml ├── ending ├── .gitignore ├── Cargo.toml ├── examples │ └── http2.rs └── src │ └── lib.rs ├── images └── rust_qr.jpg └── mid_term_rgrep ├── Cargo.toml └── src ├── error.rs ├── lib.rs └── main.rs /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | *.out 12 | -------------------------------------------------------------------------------- /01_stack_heap/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "stack_heap" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [[bin]] 7 | name = "error" 8 | path = "src/error.rs" 9 | 10 | [[bin]] 11 | name = "pointer" 12 | path = "src/pointer.rs" 13 | 14 | [[bin]] 15 | name = "string" 16 | path = "src/string.rs" 17 | 18 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 19 | 20 | [dependencies] 21 | -------------------------------------------------------------------------------- /01_stack_heap/misc/varargs.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int sum(int count, ...) { 5 | va_list ap; 6 | int i; 7 | double sum = 0; 8 | 9 | va_start(ap, count); 10 | for (i = 0; i < count; i++) { 11 | sum += va_arg(ap, int); 12 | } 13 | va_end(ap); 14 | 15 | return sum; 16 | } 17 | 18 | int main(int argc, char const *argv[]) { 19 | printf("%d\n", sum(10, 1, 2, 3)); 20 | return 0; 21 | } 22 | -------------------------------------------------------------------------------- /01_stack_heap/src/error.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let name = "Tyr".to_string(); 3 | // 对于编译错误,Rust 编译器会给出详细的错误原因,并给出建议的修复方法 4 | std::thread::spawn(|| { 5 | println!("hello1 {}", name); 6 | }); 7 | } 8 | -------------------------------------------------------------------------------- /01_stack_heap/src/pointer.rs: -------------------------------------------------------------------------------- 1 | static MAX: u32 = 0; 2 | 3 | fn foo() {} 4 | 5 | fn main() { 6 | let hello = "hello world".to_string(); 7 | let data = Box::new(1); 8 | 9 | // string literals 指向 RODATA 地址 10 | println!("RODATA: {:p}", "hello world!"); 11 | // static 变量在 DATA section 12 | println!("DATA (static var): {:p}", &MAX); 13 | // function 在 TEXT 14 | println!("TEXT (function): {:p}", foo as *const ()); 15 | // String 结构体分配在栈上,所以其引用指向一个栈地址 16 | println!("STACK (&hello): {:p}", &hello); 17 | // 需要通过解引用获取其堆上数据,然后取其引用 18 | println!("HEAP (&*hello): {:p}", &*hello); 19 | // Box 实现了 Pointer trait 无需额外解引用 20 | println!("HEAP (box impl Pointer) {:p} {:p}", data, &*data); 21 | } 22 | -------------------------------------------------------------------------------- /01_stack_heap/src/string.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let data: String = "hello".into(); 3 | 4 | let s1: &str = &data; 5 | let s2: &str = &data; 6 | let s3: &str = &data; 7 | 8 | dbg!(&s1 as *const _); 9 | dbg!(&s2 as *const _); 10 | dbg!(&s3 as *const _); 11 | 12 | dbg!(s1.as_bytes() as *const _); 13 | dbg!(s2.as_bytes() as *const _); 14 | dbg!(s3.as_bytes() as *const _); 15 | } 16 | -------------------------------------------------------------------------------- /02_concepts/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "concepts" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [[bin]] 7 | name = "shape" 8 | path = "src/shape.rs" 9 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 10 | 11 | [dependencies] 12 | -------------------------------------------------------------------------------- /02_concepts/misc/crash.c: -------------------------------------------------------------------------------- 1 | #include "stdio.h" 2 | 3 | void hello() { 4 | printf("Hello world!\n"); 5 | } 6 | 7 | int main() { 8 | char buf[1024]; 9 | void (* p)() = &hello; 10 | (*p)(); 11 | int *p1 = (int *) p; 12 | p1[1] = 0xdeadbeef; 13 | } 14 | -------------------------------------------------------------------------------- /02_concepts/misc/type.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int add_numbers(int a, int b) { 4 | int result = a + b; 5 | return result; 6 | } 7 | 8 | int main() { 9 | char c = "42"; 10 | int n = 42; 11 | 12 | // 为什么说 C 是 weakly typed 13 | int result = add_numbers(c, n); 14 | printf("%d\n", result); 15 | 16 | return 0; 17 | } -------------------------------------------------------------------------------- /02_concepts/misc/type.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | def add_numbers(a, b): 3 | return a + b 4 | 5 | if __name__ == '__main__': 6 | a = 42 7 | b = 42.0 8 | print(add_numbers(a,b)) -------------------------------------------------------------------------------- /03_grammar/.gitignore: -------------------------------------------------------------------------------- 1 | rust.md -------------------------------------------------------------------------------- /03_grammar/concepts/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "concepts" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [[bin]] 7 | name = "event" 8 | path = "src/event.rs" 9 | 10 | [[bin]] 11 | name = "fib" 12 | path = "src/fib.rs" 13 | 14 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 15 | 16 | [dependencies] 17 | -------------------------------------------------------------------------------- /03_grammar/concepts/src/fib.rs: -------------------------------------------------------------------------------- 1 | fn fib_loop(n: u8) { 2 | let mut a = 1; 3 | let mut b = 1; 4 | let mut i = 2u8; 5 | 6 | println!("\nfib_loop:"); 7 | 8 | loop { 9 | let c = a + b; 10 | a = b; 11 | b = c; 12 | i += 1; 13 | 14 | println!("next val is {}", b); 15 | 16 | if i >= n { 17 | break; 18 | } 19 | } 20 | } 21 | 22 | fn fib_while(n: u8) { 23 | let (mut a, mut b, mut i) = (1, 1, 2); 24 | 25 | println!("\nfib_while:"); 26 | 27 | while i < n { 28 | let c = a + b; 29 | a = b; 30 | b = c; 31 | i += 1; 32 | 33 | println!("next val is {}", b); 34 | } 35 | } 36 | 37 | fn fib_for(n: u8) { 38 | let (mut a, mut b) = (1, 1); 39 | 40 | println!("\nfib_for:"); 41 | 42 | for _i in 2..n { 43 | let c = a + b; 44 | a = b; 45 | b = c; 46 | println!("next val is {}", b); 47 | } 48 | } 49 | 50 | fn main() { 51 | let n = 10; 52 | fib_loop(n); 53 | fib_while(n); 54 | fib_for(n); 55 | } 56 | -------------------------------------------------------------------------------- /03_grammar/scrape_url/.gitignore: -------------------------------------------------------------------------------- 1 | rust.md 2 | -------------------------------------------------------------------------------- /03_grammar/scrape_url/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "scrape_url" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | reqwest = { version = "0.11", default-features = false, features = ["rustls-tls", "blocking"] } 10 | html2md = "0.2" 11 | -------------------------------------------------------------------------------- /03_grammar/scrape_url/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::fs; 2 | fn main() { 3 | let url = "https://www.rust-lang.org/"; 4 | let output = "rust.md"; 5 | 6 | println!("Fetching url: {}", url); 7 | let body = reqwest::blocking::get(url).unwrap().text().unwrap(); 8 | 9 | println!("Converting html to markdown..."); 10 | let md = html2md::parse_html(&body); 11 | 12 | fs::write(output, md.as_bytes()).unwrap(); 13 | println!("Converted markdown has been saved in {}.", output); 14 | } 15 | -------------------------------------------------------------------------------- /03_grammar/scrape_url_args/.gitignore: -------------------------------------------------------------------------------- 1 | rust.md 2 | -------------------------------------------------------------------------------- /03_grammar/scrape_url_args/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "scrape_url_args" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | reqwest = { version = "0.11", default-features = false, features = ["rustls-tls", "blocking"] } 10 | html2md = "0.2" 11 | -------------------------------------------------------------------------------- /03_grammar/scrape_url_args/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::fs; 2 | fn main() -> Result<(), Box> { 3 | for arg in std::env::args() { 4 | println!("{}", arg); 5 | } 6 | let args = std::env::args().skip(1).collect::>(); 7 | 8 | if args.is_empty() { 9 | println!("Usage: scrape_url_args "); 10 | std::process::exit(1); 11 | } 12 | 13 | let url = &args[0]; 14 | let output = &args[1]; 15 | 16 | println!("Fetching url: {}", url); 17 | let body = reqwest::blocking::get(url)?.text()?; 18 | 19 | println!("Converting html to markdown..."); 20 | let md = html2md::parse_html(&body); 21 | 22 | fs::write(output, md.as_bytes())?; 23 | println!("Converted markdown has been saved in {}.", output); 24 | 25 | Ok(()) 26 | } 27 | -------------------------------------------------------------------------------- /03_grammar/scrape_url_with_error_handling/.gitignore: -------------------------------------------------------------------------------- 1 | rust.md 2 | -------------------------------------------------------------------------------- /03_grammar/scrape_url_with_error_handling/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "scrape_url_with_error_handling" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | reqwest = { version = "0.11", default-features = false, features = ["rustls-tls", "blocking"] } 10 | html2md = "0.2" 11 | -------------------------------------------------------------------------------- /03_grammar/scrape_url_with_error_handling/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::fs; 2 | fn main() -> Result<(), Box> { 3 | let url = "https://www.rust-lang.org/"; 4 | let output = "rust.md"; 5 | 6 | println!("Fetching url: {}", url); 7 | let body = reqwest::blocking::get(url)?.text()?; 8 | 9 | println!("Converting html to markdown..."); 10 | let md = html2md::parse_html(&body); 11 | 12 | fs::write(output, md.as_bytes())?; 13 | println!("Converted markdown has been saved in {}.", output); 14 | 15 | Ok(()) 16 | } 17 | -------------------------------------------------------------------------------- /04_httpie/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "httpie" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [[example]] 9 | name = "cli" 10 | 11 | [[example]] 12 | name = "cli_verify" 13 | 14 | [[example]] 15 | name = "cli_get" 16 | 17 | [dependencies] 18 | anyhow = "1" # 错误处理 19 | clap = { version = "3", features = ["derive"] } # 命令行解析 20 | colored = "2" # 命令终端多彩显示 21 | jsonxf = "1.1" # JSON pretty print 格式化 22 | mime = "0.3" # 处理 mime 类型 23 | # reqwest 默认使用 openssl,有些 linux 用户如果没有安装好 openssl 会无法编译,这里我改成了使用 rustls 24 | reqwest = { version = "0.11", default-features = false, features = ["json", "rustls-tls"] } # HTTP 客户端 25 | tokio = { version = "1", features = ["full"] } # 异步处理库 26 | syntect = "4" 27 | -------------------------------------------------------------------------------- /04_httpie/examples/cli.rs: -------------------------------------------------------------------------------- 1 | use clap::Parser; 2 | 3 | // 定义 httpie 的 CLI 的主入口,它包含若干个子命令 4 | // 下面 /// 的注释是文档,clap 会将其作为 CLI 的帮助 5 | 6 | /// A naive httpie implementation with Rust, can you imagine how easy it is? 7 | #[derive(Parser, Debug)] 8 | #[clap(version = "1.0", author = "Tyr Chen ")] 9 | struct Opts { 10 | #[clap(subcommand)] 11 | subcmd: SubCommand, 12 | } 13 | 14 | // 子命令分别对应不同的 HTTP 方法,目前只支持 get / post 15 | #[derive(Parser, Debug)] 16 | enum SubCommand { 17 | Get(Get), 18 | Post(Post), 19 | // 我们暂且不支持其它 HTTP 方法 20 | } 21 | 22 | // get 子命令 23 | 24 | /// feed get with an url and we will retrieve the response for you 25 | #[derive(Parser, Debug)] 26 | struct Get { 27 | /// HTTP 请求的 URL 28 | url: String, 29 | } 30 | 31 | // post 子命令。需要输入一个 url,和若干个可选的 key=value,用于提供 json body 32 | 33 | /// feed post with an url and optional key=value pairs. We will post the data 34 | /// as JSON, and retrieve the response for you 35 | #[derive(Parser, Debug)] 36 | struct Post { 37 | /// HTTP 请求的 URL 38 | url: String, 39 | /// HTTP 请求的 body 40 | body: Vec, 41 | } 42 | 43 | fn main() { 44 | let opts: Opts = Opts::parse(); 45 | println!("{:?}", opts); 46 | } 47 | -------------------------------------------------------------------------------- /05_thumbor/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "thumbor" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [[bin]] 7 | name = "server1" 8 | path = "src/server1.rs" 9 | 10 | [[bin]] 11 | name = "server2" 12 | path = "src/server2.rs" 13 | 14 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 15 | 16 | [dependencies] 17 | axum = "0.2" # web 服务器 18 | anyhow = "1" # 错误处理 19 | base64 = "0.13" # base64 编码/解码 20 | bytes = "1" # 处理字节流 21 | image = "0.23" # 处理图片 22 | lazy_static = "1" # 通过宏更方便地初始化静态变量 23 | lru = "0.6" # LRU 缓存 24 | percent-encoding = "2" # url 编码/解码 25 | photon-rs = "0.3" # 图片效果 26 | prost = "0.8" # protobuf 处理 27 | reqwest = { version = "0.11", default-features = false, features = ["json", "rustls-tls"] } # HTTP 客户端 28 | serde = { version = "1", features = ["derive"] } # 序列化/反序列化数据 29 | tokio = { version = "1", features = ["full"] } # 异步处理 30 | tower = { version = "0.4", features = ["util", "timeout", "load-shed", "limit"] } # 服务处理及中间件 31 | tower-http = { version = "0.1", features = ["add-extension", "compression-full", "trace" ] } # http 中间件 32 | tracing = "0.1" # 日志和追踪 33 | tracing-subscriber = "0.2" # 日志和追踪 34 | 35 | [build-dependencies] 36 | prost-build = "0.8" 37 | -------------------------------------------------------------------------------- /05_thumbor/build.rs: -------------------------------------------------------------------------------- 1 | use std::process::Command; 2 | 3 | fn main() { 4 | let build_enabled = option_env!("BUILD_PROTO") 5 | .map(|v| v == "1") 6 | .unwrap_or(false); 7 | 8 | if !build_enabled { 9 | println!("=== Skipped compiling protos ==="); 10 | return; 11 | } 12 | 13 | prost_build::Config::new() 14 | .out_dir("src/pb") 15 | .compile_protos(&["abi.proto"], &["."]) 16 | .unwrap(); 17 | Command::new("cargo") 18 | .args(&["fmt", "--", "src/*.rs"]) 19 | .status() 20 | .expect("cargo fmt failed"); 21 | } 22 | -------------------------------------------------------------------------------- /05_thumbor/rust-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/405-studio/geektime-rust/3c57ef418118eacc24d2e35acb470aa4e6a6a672/05_thumbor/rust-logo.png -------------------------------------------------------------------------------- /05_thumbor/src/engine/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::pb::Spec; 2 | use image::ImageOutputFormat; 3 | 4 | mod photon; 5 | pub use photon::Photon; 6 | 7 | // Engine trait:未来可以添加更多的 engine,主流程只需要替换 engine 8 | pub trait Engine { 9 | // 对 engine 按照 specs 进行一系列有序的处理 10 | fn apply(&mut self, specs: &[Spec]); 11 | // 从 engine 中生成目标图片,注意这里用的是 self,而非 self 的引用 12 | fn generate(self, format: ImageOutputFormat) -> Vec; 13 | } 14 | 15 | // SpecTransform:未来如果添加更多的 spec,只需要实现它即可 16 | pub trait SpecTransform { 17 | // 对图片使用 op 做 transform 18 | fn transform(&mut self, op: T); 19 | } 20 | -------------------------------------------------------------------------------- /05_thumbor/src/server1.rs: -------------------------------------------------------------------------------- 1 | use axum::{extract::Path, handler::get, http::StatusCode, Router}; 2 | use percent_encoding::percent_decode_str; 3 | use serde::Deserialize; 4 | 5 | mod pb; 6 | 7 | use pb::*; 8 | 9 | #[derive(Deserialize)] 10 | struct Params { 11 | spec: String, 12 | url: String, 13 | } 14 | 15 | #[tokio::main] 16 | async fn main() { 17 | // 初始化 tracing 18 | tracing_subscriber::fmt::init(); 19 | 20 | // 构建路由 21 | let app = Router::new() 22 | // `GET /image` 会执行 generate 函数,并把 spec 和 url 传递过去 23 | .route("/image/:spec/:url", get(generate)); 24 | 25 | // 运行 web 服务器 26 | let addr = "127.0.0.1:3000".parse().unwrap(); 27 | tracing::debug!("listening on {}", addr); 28 | axum::Server::bind(&addr) 29 | .serve(app.into_make_service()) 30 | .await 31 | .unwrap(); 32 | } 33 | 34 | // 目前我们就只把参数解析出来 35 | async fn generate(Path(Params { spec, url }): Path) -> Result { 36 | let url = percent_decode_str(&url).decode_utf8_lossy(); 37 | let spec: ImageSpec = spec 38 | .as_str() 39 | .try_into() 40 | .map_err(|_| StatusCode::BAD_REQUEST)?; 41 | Ok(format!("url: {}\n spec: {:#?}", url, spec)) 42 | } 43 | -------------------------------------------------------------------------------- /06_queryer/Cargo.toml.bak: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | members = [ 4 | "queryer", 5 | "queryer-js", 6 | "queryer-py", 7 | "data-viewer/src-tauri" 8 | ] 9 | -------------------------------------------------------------------------------- /06_queryer/README.md: -------------------------------------------------------------------------------- 1 | # Queryer 2 | 3 | 因为要做 github action 的缘故,06_queryer 被放在外层的 workspace 包裹,为避免冲突这里的 Cargo.toml 就重命名为 Cargo.toml.bak,如果你需要单独编译这个目录,可以把 .bak 改回去。 4 | -------------------------------------------------------------------------------- /06_queryer/data-viewer/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /06_queryer/data-viewer/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "git.ignoreLimitWarning": true 3 | } 4 | -------------------------------------------------------------------------------- /06_queryer/data-viewer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "data-viewer", 3 | "scripts": { 4 | "tauri": "tauri dev" 5 | }, 6 | "devDependencies": { 7 | "@tauri-apps/cli": "^1.0.0-beta.10" 8 | }, 9 | "dependencies": { 10 | "@tauri-apps/api": "^1.0.0-beta.8" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /06_queryer/data-viewer/src-tauri/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | WixTools 5 | -------------------------------------------------------------------------------- /06_queryer/data-viewer/src-tauri/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "app" 3 | version = "0.1.0" 4 | description = "A Tauri App" 5 | authors = ["you"] 6 | license = "" 7 | repository = "" 8 | default-run = "app" 9 | edition = "2021" 10 | build = "src/build.rs" 11 | 12 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 13 | 14 | [build-dependencies] 15 | tauri-build = { version = "1.0.0-beta.4" } 16 | 17 | [dependencies] 18 | anyhow = "1" 19 | serde_json = "1" 20 | queryer = { path = "../../queryer" } 21 | serde = { version = "1.0", features = ["derive"] } 22 | tauri = { version = "1.0.0-beta.8", features = ["api-all"] } 23 | 24 | [features] 25 | default = [ "custom-protocol" ] 26 | custom-protocol = [ "tauri/custom-protocol" ] 27 | -------------------------------------------------------------------------------- /06_queryer/data-viewer/src-tauri/icons/128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/405-studio/geektime-rust/3c57ef418118eacc24d2e35acb470aa4e6a6a672/06_queryer/data-viewer/src-tauri/icons/128x128.png -------------------------------------------------------------------------------- /06_queryer/data-viewer/src-tauri/icons/128x128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/405-studio/geektime-rust/3c57ef418118eacc24d2e35acb470aa4e6a6a672/06_queryer/data-viewer/src-tauri/icons/128x128@2x.png -------------------------------------------------------------------------------- /06_queryer/data-viewer/src-tauri/icons/32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/405-studio/geektime-rust/3c57ef418118eacc24d2e35acb470aa4e6a6a672/06_queryer/data-viewer/src-tauri/icons/32x32.png -------------------------------------------------------------------------------- /06_queryer/data-viewer/src-tauri/icons/Square107x107Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/405-studio/geektime-rust/3c57ef418118eacc24d2e35acb470aa4e6a6a672/06_queryer/data-viewer/src-tauri/icons/Square107x107Logo.png -------------------------------------------------------------------------------- /06_queryer/data-viewer/src-tauri/icons/Square142x142Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/405-studio/geektime-rust/3c57ef418118eacc24d2e35acb470aa4e6a6a672/06_queryer/data-viewer/src-tauri/icons/Square142x142Logo.png -------------------------------------------------------------------------------- /06_queryer/data-viewer/src-tauri/icons/Square150x150Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/405-studio/geektime-rust/3c57ef418118eacc24d2e35acb470aa4e6a6a672/06_queryer/data-viewer/src-tauri/icons/Square150x150Logo.png -------------------------------------------------------------------------------- /06_queryer/data-viewer/src-tauri/icons/Square284x284Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/405-studio/geektime-rust/3c57ef418118eacc24d2e35acb470aa4e6a6a672/06_queryer/data-viewer/src-tauri/icons/Square284x284Logo.png -------------------------------------------------------------------------------- /06_queryer/data-viewer/src-tauri/icons/Square30x30Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/405-studio/geektime-rust/3c57ef418118eacc24d2e35acb470aa4e6a6a672/06_queryer/data-viewer/src-tauri/icons/Square30x30Logo.png -------------------------------------------------------------------------------- /06_queryer/data-viewer/src-tauri/icons/Square310x310Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/405-studio/geektime-rust/3c57ef418118eacc24d2e35acb470aa4e6a6a672/06_queryer/data-viewer/src-tauri/icons/Square310x310Logo.png -------------------------------------------------------------------------------- /06_queryer/data-viewer/src-tauri/icons/Square44x44Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/405-studio/geektime-rust/3c57ef418118eacc24d2e35acb470aa4e6a6a672/06_queryer/data-viewer/src-tauri/icons/Square44x44Logo.png -------------------------------------------------------------------------------- /06_queryer/data-viewer/src-tauri/icons/Square71x71Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/405-studio/geektime-rust/3c57ef418118eacc24d2e35acb470aa4e6a6a672/06_queryer/data-viewer/src-tauri/icons/Square71x71Logo.png -------------------------------------------------------------------------------- /06_queryer/data-viewer/src-tauri/icons/Square89x89Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/405-studio/geektime-rust/3c57ef418118eacc24d2e35acb470aa4e6a6a672/06_queryer/data-viewer/src-tauri/icons/Square89x89Logo.png -------------------------------------------------------------------------------- /06_queryer/data-viewer/src-tauri/icons/StoreLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/405-studio/geektime-rust/3c57ef418118eacc24d2e35acb470aa4e6a6a672/06_queryer/data-viewer/src-tauri/icons/StoreLogo.png -------------------------------------------------------------------------------- /06_queryer/data-viewer/src-tauri/icons/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/405-studio/geektime-rust/3c57ef418118eacc24d2e35acb470aa4e6a6a672/06_queryer/data-viewer/src-tauri/icons/icon.icns -------------------------------------------------------------------------------- /06_queryer/data-viewer/src-tauri/icons/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/405-studio/geektime-rust/3c57ef418118eacc24d2e35acb470aa4e6a6a672/06_queryer/data-viewer/src-tauri/icons/icon.ico -------------------------------------------------------------------------------- /06_queryer/data-viewer/src-tauri/icons/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/405-studio/geektime-rust/3c57ef418118eacc24d2e35acb470aa4e6a6a672/06_queryer/data-viewer/src-tauri/icons/icon.png -------------------------------------------------------------------------------- /06_queryer/data-viewer/src-tauri/rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 100 2 | hard_tabs = false 3 | tab_spaces = 2 4 | newline_style = "Auto" 5 | use_small_heuristics = "Default" 6 | reorder_imports = true 7 | reorder_modules = true 8 | remove_nested_parens = true 9 | edition = "2021" 10 | merge_derives = true 11 | use_try_shorthand = false 12 | use_field_init_shorthand = false 13 | force_explicit_abi = true 14 | # imports_granularity = "Crate" 15 | -------------------------------------------------------------------------------- /06_queryer/data-viewer/src-tauri/src/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | tauri_build::build() 3 | } 4 | -------------------------------------------------------------------------------- /06_queryer/data-viewer/src-tauri/src/main.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr( 2 | all(not(debug_assertions), target_os = "windows"), 3 | windows_subsystem = "windows" 4 | )] 5 | 6 | #[tauri::command] 7 | fn example_sql() -> String { 8 | queryer::example_sql() 9 | } 10 | 11 | #[tauri::command] 12 | async fn query(sql: String) -> Result { 13 | let data = queryer::query(&sql).await.map_err(|err| err.to_string())?; 14 | Ok(data.to_csv().map_err(|err| err.to_string())?) 15 | } 16 | 17 | fn main() { 18 | tauri::Builder::default() 19 | .invoke_handler(tauri::generate_handler![example_sql, query]) 20 | .run(tauri::generate_context!()) 21 | .expect("error while running tauri application"); 22 | } 23 | -------------------------------------------------------------------------------- /06_queryer/queryer-js/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | index.node 3 | **/node_modules 4 | **/.DS_Store 5 | npm-debug.log* 6 | -------------------------------------------------------------------------------- /06_queryer/queryer-js/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "queryer-js" 3 | version = "0.1.0" 4 | license = "ISC" 5 | edition = "2021" 6 | exclude = ["index.node"] 7 | 8 | [lib] 9 | crate-type = ["cdylib"] 10 | 11 | [dependencies] 12 | anyhow = "1" 13 | queryer = { path = "../queryer" } 14 | tokio = { version = "1", features = ["full"] } 15 | 16 | [dependencies.neon] 17 | version = "0.9" 18 | default-features = false 19 | features = ["napi-6"] 20 | -------------------------------------------------------------------------------- /06_queryer/queryer-js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "queryer-js", 3 | "version": "0.1.0", 4 | "description": "", 5 | "main": "index.node", 6 | "scripts": { 7 | "build": "cargo-cp-artifact -nc index.node -- cargo build --message-format=json-render-diagnostics", 8 | "build-debug": "npm run build --", 9 | "build-release": "npm run build -- --release", 10 | "install": "npm run build-release", 11 | "test": "cargo test" 12 | }, 13 | "author": "", 14 | "license": "ISC", 15 | "devDependencies": { 16 | "cargo-cp-artifact": "^0.1" 17 | } 18 | } -------------------------------------------------------------------------------- /06_queryer/queryer-js/src/lib.rs: -------------------------------------------------------------------------------- 1 | use neon::prelude::*; 2 | 3 | pub fn example_sql(mut cx: FunctionContext) -> JsResult { 4 | Ok(cx.string(queryer::example_sql())) 5 | } 6 | 7 | fn query(mut cx: FunctionContext) -> JsResult { 8 | let sql = cx.argument::(0)?.value(&mut cx); 9 | let output = match cx.argument::(1) { 10 | Ok(v) => v.value(&mut cx), 11 | Err(_) => "csv".to_string(), 12 | }; 13 | let rt = tokio::runtime::Runtime::new().unwrap(); 14 | let data = rt.block_on(async { queryer::query(sql).await.unwrap() }); 15 | 16 | match output.as_str() { 17 | "csv" => Ok(cx.string(data.to_csv().unwrap())), 18 | v => cx.throw_type_error(format!("Output type {} not supported", v)), 19 | } 20 | } 21 | 22 | #[neon::main] 23 | fn main(mut cx: ModuleContext) -> NeonResult<()> { 24 | cx.export_function("example_sql", example_sql)?; 25 | cx.export_function("query", query)?; 26 | Ok(()) 27 | } 28 | -------------------------------------------------------------------------------- /06_queryer/queryer-js/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | cargo-cp-artifact@^0.1: 6 | version "0.1.5" 7 | resolved "https://registry.yarnpkg.com/cargo-cp-artifact/-/cargo-cp-artifact-0.1.5.tgz#2c35f7d658f22acfe9b1fa2774344d9a389efd14" 8 | integrity sha512-mWwNdfrEyvMPxDHoAhbCYwQBNP3RyW9bV+yi5VaaHtVZqoDXbGU5JQF5qG7s65SJhqP7HIVAQD7wZM6Fk2COuw== 9 | -------------------------------------------------------------------------------- /06_queryer/queryer-py/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "queryer_py" # Python 模块需要用下划线 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | 7 | [lib] 8 | crate-type = ["cdylib"] # 使用 cdylib 类型 9 | 10 | [dependencies] 11 | queryer = { path = "../queryer" } # 引入 queryer 12 | tokio = { version = "1", features = ["full"] } 13 | 14 | [dependencies.pyo3] 15 | version = "0.14" 16 | features = ["extension-module"] 17 | 18 | [build-dependencies] 19 | pyo3-build-config = "0.14" 20 | -------------------------------------------------------------------------------- /06_queryer/queryer-py/README.md: -------------------------------------------------------------------------------- 1 | # 构建 Python 编译环境 2 | 3 | 创建 virtual env,然后用 `maturin develop` 构建: 4 | ```bash 5 | cd queryer-py 6 | python -m venv .env 7 | source .env/bin/activate 8 | pip install maturin 9 | maturin develop 10 | ``` 11 | 12 | 之后可以使用: 13 | 14 | ```ipython 15 | In [1]: import queryer_py 16 | 17 | In [2]: sql = queryer_py.example_sql() 18 | 19 | In [3]: print(queryer_py.query(sql)) 20 | name,total_cases,new_cases,total_deaths,new_deaths 21 | India,32649947.0,46759.0,437370.0,509.0 22 | Iran,4869414.0,36279.0,105287.0,571.0 23 | Africa,7695475.0,33957.0,193394.0,764.0 24 | South America,36768062.0,33853.0,1126593.0,1019.0 25 | Brazil,20703906.0,27345.0,578326.0,761.0 26 | Mexico,3311317.0,19556.0,257150.0,863.0 27 | ``` 28 | -------------------------------------------------------------------------------- /06_queryer/queryer-py/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | pyo3_build_config::add_extension_module_link_args(); 3 | } 4 | -------------------------------------------------------------------------------- /06_queryer/queryer-py/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::needless_option_as_deref)] 2 | use pyo3::{exceptions, prelude::*}; 3 | 4 | #[pyfunction] 5 | pub fn example_sql() -> PyResult { 6 | Ok(queryer::example_sql()) 7 | } 8 | 9 | #[pyfunction] 10 | pub fn query(sql: &str, output: Option<&str>) -> PyResult { 11 | let rt = tokio::runtime::Runtime::new().unwrap(); 12 | let data = rt.block_on(async { queryer::query(sql).await.unwrap() }); 13 | match output { 14 | Some("csv") | None => Ok(data.to_csv().unwrap()), 15 | Some(v) => Err(exceptions::PyTypeError::new_err(format!( 16 | "Output type {} not supported", 17 | v 18 | ))), 19 | } 20 | } 21 | 22 | #[pymodule] 23 | fn queryer_py(_py: Python, m: &PyModule) -> PyResult<()> { 24 | m.add_function(wrap_pyfunction!(query, m)?)?; 25 | m.add_function(wrap_pyfunction!(example_sql, m)?)?; 26 | Ok(()) 27 | } 28 | -------------------------------------------------------------------------------- /06_queryer/queryer/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "queryer" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [[example]] 7 | name = "dialect" 8 | 9 | [[example]] 10 | name = "covid" 11 | 12 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 13 | 14 | [dependencies] 15 | anyhow = "1" # 错误处理,其实对于库我们应该用 thiserror,但这里简单期间就不节外生枝了 16 | async-trait = "0.1" # 允许 trait 里有 async fn 17 | sqlparser = "0.10" # SQL 解析器 18 | polars = { version = "0.15", features = ["json", "lazy"] } # DataFrame 库 19 | reqwest = { version = "0.11", default-features = false, features = ["json", "rustls-tls"] } # 我们的老朋友 HTTP 客户端 20 | tokio = { version = "1", features = ["fs"]} # 我们的老朋友异步库,我们这里需要异步文件处理 21 | tracing = "0.1" # 日志处理 22 | 23 | [dev-dependencies] 24 | tracing-subscriber = "0.2" # 日志处理 25 | tokio = { version = "1", features = ["full"]} # 在 example 下我们需要更多的 tokio feature 26 | -------------------------------------------------------------------------------- /06_queryer/queryer/examples/covid.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use queryer::query; 3 | 4 | #[tokio::main] 5 | async fn main() -> Result<()> { 6 | tracing_subscriber::fmt::init(); 7 | 8 | let url = "https://raw.githubusercontent.com/owid/covid-19-data/master/public/data/latest/owid-covid-latest.csv"; 9 | 10 | // 使用 sql 从 URL 里获取数据 11 | let sql = format!( 12 | "SELECT location name, total_cases, new_cases, total_deaths, new_deaths \ 13 | FROM {} where new_deaths >= 500 ORDER BY new_cases DESC", 14 | url 15 | ); 16 | let df1 = query(sql).await?; 17 | println!("{:?}", df1); 18 | 19 | Ok(()) 20 | } 21 | -------------------------------------------------------------------------------- /06_queryer/queryer/examples/dialect.rs: -------------------------------------------------------------------------------- 1 | use sqlparser::{dialect::GenericDialect, parser::Parser}; 2 | 3 | fn main() { 4 | tracing_subscriber::fmt::init(); 5 | 6 | let sql = "SELECT a a1, b, 123, myfunc(b), * \ 7 | FROM data_source \ 8 | WHERE a > b AND b < 100 AND c BETWEEN 10 AND 20 \ 9 | ORDER BY a DESC, b \ 10 | LIMIT 50 OFFSET 10"; 11 | 12 | let ast = Parser::parse_sql(&GenericDialect::default(), sql); 13 | println!("{:#?}", ast); 14 | } 15 | -------------------------------------------------------------------------------- /06_queryer/queryer/src/loader.rs: -------------------------------------------------------------------------------- 1 | use crate::DataSet; 2 | use anyhow::Result; 3 | use polars::prelude::*; 4 | use std::io::Cursor; 5 | 6 | pub trait Load { 7 | type Error; 8 | fn load(self) -> Result; 9 | } 10 | 11 | #[derive(Debug)] 12 | #[non_exhaustive] 13 | pub enum Loader { 14 | Csv(CsvLoader), 15 | } 16 | 17 | #[derive(Default, Debug)] 18 | pub struct CsvLoader(pub(crate) String); 19 | 20 | impl Loader { 21 | pub fn load(self) -> Result { 22 | match self { 23 | Loader::Csv(csv) => csv.load(), 24 | } 25 | } 26 | } 27 | 28 | pub fn detect_content(data: String) -> Loader { 29 | // TODO: 内容检测 30 | Loader::Csv(CsvLoader(data)) 31 | } 32 | 33 | impl Load for CsvLoader { 34 | type Error = anyhow::Error; 35 | 36 | fn load(self) -> Result { 37 | let df = CsvReader::new(Cursor::new(self.0)) 38 | .infer_schema(Some(16)) 39 | .finish()?; 40 | Ok(DataSet(df)) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /07_ownership/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ownership" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [[bin]] 7 | name = "ownership1" 8 | path = "src/ownership1.rs" 9 | 10 | [[bin]] 11 | name = "ownership_error" 12 | path = "src/ownership_error.rs" 13 | 14 | [[bin]] 15 | name = "check_copy" 16 | path = "src/check_copy.rs" 17 | 18 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 19 | 20 | [dependencies] 21 | -------------------------------------------------------------------------------- /07_ownership/src/check_copy.rs: -------------------------------------------------------------------------------- 1 | fn is_copy() {} 2 | 3 | fn types_impl_copy_trait() { 4 | is_copy::(); 5 | is_copy::(); 6 | 7 | // 所有整数类型都是 copy 8 | is_copy::(); 9 | is_copy::(); 10 | is_copy::(); 11 | is_copy::(); 12 | 13 | // 函数指针是 copy 14 | is_copy::(); 15 | 16 | // 裸指针是 copy 17 | is_copy::<*const String>(); 18 | is_copy::<*mut String>(); 19 | 20 | // 不可变引用是 copy 21 | is_copy::<&[Vec]>(); 22 | is_copy::<&String>(); 23 | 24 | // 对于数组/元组/,如果其内部类型是 copy 那么它们也是 copy 25 | is_copy::<[u8; 4]>(); 26 | is_copy::<(&str, &str)>(); 27 | } 28 | 29 | fn types_not_impl_copy_trait() { 30 | // DST 类型不是 copy 31 | // is_copy::(); 32 | // is_copy::<[u8]>(); 33 | 34 | // 有堆内存的类型不是 copy 35 | // is_copy::>(); 36 | // is_copy::(); 37 | 38 | // 可变引用不是 copy 39 | // is_copy::<&mut String>(); 40 | 41 | // 对于数组/元组/,如果其内部类型是不是 copy,那么它们也不是 copy 42 | // is_copy::<[Vec; 4]>(); 43 | // is_copy::<(String, u32)>(); 44 | } 45 | 46 | fn main() { 47 | types_impl_copy_trait(); 48 | types_not_impl_copy_trait(); 49 | } 50 | -------------------------------------------------------------------------------- /07_ownership/src/ownership1.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let data = vec![10, 42, 9, 8]; 3 | let v = 42; 4 | 5 | // Try using `match` here? 6 | if let Some(pos) = find_pos(data, v) { 7 | println!("Found {} at {}", v, pos); 8 | } else { 9 | println!("{} not found", v); 10 | } 11 | } 12 | 13 | fn find_pos(data: Vec, v: u32) -> Option { 14 | for (pos, item) in data.iter().enumerate() { 15 | if *item == v { 16 | return Some(pos); 17 | } 18 | } 19 | 20 | None 21 | } 22 | -------------------------------------------------------------------------------- /07_ownership/src/ownership_error.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let data = vec![1, 2, 3, 4]; 3 | let data1 = data; 4 | println!("sum of data1: {}", sum(data1)); 5 | // 下面两句无法编译通过 6 | // println!("data1: {:?}", data1); 7 | // println!("sum of data: {}", sum(data)); 8 | } 9 | 10 | fn sum(data: Vec) -> u32 { 11 | data.iter().sum() 12 | } 13 | -------------------------------------------------------------------------------- /08_borrow/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "borrow" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [[bin]] 7 | name = "ownership_reference" 8 | path = "src/ownership_reference.rs" 9 | 10 | [[bin]] 11 | name = "local_ref" 12 | path = "src/local_ref.rs" 13 | 14 | [[bin]] 15 | name = "heap_reference_stack" 16 | path = "src/heap_reference_stack.rs" 17 | 18 | [[bin]] 19 | name = "heap_reference_stack_outlive" 20 | path = "src/heap_reference_stack_outlive.rs" 21 | 22 | [[bin]] 23 | name = "multiple_mut_ref" 24 | path = "src/multiple_mut_ref.rs" 25 | 26 | [[bin]] 27 | name = "multiple_mut_ref1" 28 | path = "src/multiple_mut_ref1.rs" 29 | 30 | [[bin]] 31 | name = "manual_drop" 32 | path = "src/manual_drop.rs" 33 | 34 | [[bin]] 35 | name = "vec_grow" 36 | path = "src/vec_grow.rs" 37 | 38 | [[bin]] 39 | name = "reborrow" 40 | path = "src/reborrow.rs" 41 | 42 | [[bin]] 43 | name = "reborrow1" 44 | path = "src/reborrow1.rs" 45 | 46 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 47 | 48 | [dependencies] 49 | -------------------------------------------------------------------------------- /08_borrow/misc/multiple_mut_ref.py: -------------------------------------------------------------------------------- 1 | if __name__ == "__main__": 2 | data = [1, 2] 3 | for item in data: 4 | data.append(item + 1) 5 | print(item) 6 | print(data) 7 | -------------------------------------------------------------------------------- /08_borrow/misc/reference.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int * lcal_ref() 4 | { 5 | int a = 42; 6 | return &a; 7 | } 8 | 9 | int main() 10 | { 11 | int* p = lcal_ref(); 12 | printf("%i\n", *p); 13 | *p = 256; 14 | printf("%i\n", *p); 15 | } 16 | -------------------------------------------------------------------------------- /08_borrow/src/heap_reference_stack.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let mut data: Vec<&u32> = Vec::new(); 3 | let v = 42; 4 | data.push(&v); 5 | println!("data: {:?}", data); 6 | } 7 | -------------------------------------------------------------------------------- /08_borrow/src/heap_reference_stack_outlive.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let mut data: Vec<&u32> = Vec::new(); 3 | push_local_ref(&mut data); 4 | println!("data: {:?}", data); 5 | } 6 | 7 | #[allow(unused_variables)] 8 | fn push_local_ref(data: &mut Vec<&u32>) { 9 | let v = 42; 10 | // v 生命周期不够长,如果注释掉会编译不过 11 | // data.push(&v); 12 | } 13 | -------------------------------------------------------------------------------- /08_borrow/src/local_ref.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let r = local_ref(); 3 | println!("r: {:p}", r); 4 | } 5 | 6 | #[allow(unused_variables)] 7 | fn local_ref<'a>() -> &'a i32 { 8 | let a = 42; 9 | // 不能返回对局部变量 a 的引用 10 | // &a 11 | todo!(); 12 | } 13 | -------------------------------------------------------------------------------- /08_borrow/src/manual_drop.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | mem::ManuallyDrop, 3 | ops::{Deref, DerefMut}, 4 | }; 5 | 6 | #[derive(Debug)] 7 | struct MyString(String); 8 | 9 | impl From<&str> for MyString { 10 | fn from(s: &str) -> Self { 11 | MyString(s.to_string()) 12 | } 13 | } 14 | 15 | impl Drop for MyString { 16 | fn drop(&mut self) { 17 | println!("Going to drop: {}", self.0); 18 | } 19 | } 20 | 21 | impl Deref for MyString { 22 | type Target = String; 23 | 24 | fn deref(&self) -> &Self::Target { 25 | &self.0 26 | } 27 | } 28 | 29 | impl DerefMut for MyString { 30 | fn deref_mut(&mut self) -> &mut Self::Target { 31 | &mut self.0 32 | } 33 | } 34 | 35 | fn main() { 36 | // 使用 ManuallyDrop 封装数据结构使其不进行自动 drop 37 | let mut s = ManuallyDrop::new(MyString::from("Hello World!")); 38 | 39 | // ManuallyDrop 使用了 Deref trait 指向 T,所以可以当 MyString 使用,MyString 又可以当 String 用 40 | s.truncate(5); 41 | println!("s: {:?}", s); 42 | 43 | // 如果没有这句,s 不会在 scope 结束时被自动 drop(你可以注掉试一下) 44 | // 如果我们想让它可以自动 drop,可以用 into_inner 45 | let _: MyString = ManuallyDrop::into_inner(s); 46 | } 47 | -------------------------------------------------------------------------------- /08_borrow/src/multiple_mut_ref.rs: -------------------------------------------------------------------------------- 1 | #[allow(unused_variables, unused_mut)] 2 | 3 | fn main() { 4 | let mut data = vec![1, 2, 3]; 5 | 6 | // Rust 下,根据所有权规则,你无法同时拥有多个可变引用 7 | // for item in data.iter_mut() { 8 | // data.push(*item + 1); 9 | // } 10 | } 11 | -------------------------------------------------------------------------------- /08_borrow/src/multiple_mut_ref1.rs: -------------------------------------------------------------------------------- 1 | #[allow(unused_mut)] 2 | 3 | fn main() { 4 | let mut data = vec![1, 2, 3]; 5 | let data1 = vec![&data[0]]; 6 | println!("data[0]: {:p}", &data[0]); 7 | 8 | // Rust 下,不能同时拥有可变引用和只读引用 9 | // for i in 0..100 { 10 | // data.push(i); 11 | // } 12 | 13 | println!("data[0]: {:p}", &data[0]); 14 | println!("boxed: {:p}", &data1); 15 | } 16 | -------------------------------------------------------------------------------- /08_borrow/src/ownership_reference.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let data = vec![1, 2, 3, 4]; 3 | let data1 = &data; 4 | // 值的地址是什么?引用的地址又是什么? 5 | println!( 6 | "addr of value: {:p}({:p}), addr of data {:p}, data1: {:p}", 7 | &data, data1, &&data, &data1 8 | ); 9 | println!("sum of data1: {}", sum(data1)); 10 | 11 | // 堆上数据的地址是什么? 12 | println!( 13 | "addr of items: [{:p}, {:p}, {:p}, {:p}]", 14 | &data[0], &data[1], &data[2], &data[3] 15 | ); 16 | } 17 | 18 | fn sum(data: &[u32]) -> u32 { 19 | // 值的地址会改变么?引用的地址会改变么? 20 | println!("addr of value: {:p}, addr of ref: {:p}", data, &data); 21 | data.iter().sum() 22 | } 23 | -------------------------------------------------------------------------------- /08_borrow/src/reborrow.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let mut x = 42; 3 | 4 | let r1 = &mut x; 5 | // reborrow 可以通过 6 | let r2 = &*r1; 7 | // &x 不可以 8 | // let r2 = &x; 9 | 10 | println!("r1: {:p}, r2: {:p}", &r1, &r2); 11 | 12 | *r1 += 1; 13 | } 14 | -------------------------------------------------------------------------------- /08_borrow/src/reborrow1.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let mut data = vec![1, 2, 3, 4]; 3 | let b = &mut data; 4 | println!("addr of the ref b: {:p}", &b); 5 | println!("sum of data1: {}", sum(b)); 6 | // ok 7 | println!("{:?}", b); 8 | } 9 | 10 | fn sum(v: &mut Vec) -> i32 { 11 | println!("addr of the ref v: {:p}", &v); 12 | v.iter().sum() 13 | } 14 | -------------------------------------------------------------------------------- /08_borrow/src/vec_grow.rs: -------------------------------------------------------------------------------- 1 | use std::mem; 2 | 3 | fn main() { 4 | // capacity 是 1, len 是 0 5 | let mut v = vec![1]; 6 | // capacity 是 8, len 是 0 7 | let v1: Vec = Vec::with_capacity(8); 8 | 9 | print_vec("v1", v1); 10 | 11 | // 我们先打印 heap 地址,然后看看添加内容是否会导致堆重分配 12 | println!("heap start: {:p}", &v[0] as *const i32); 13 | 14 | extend_vec(&mut v); 15 | 16 | // heap 地址改变了!这就是为什么可变引用和不可变引用不能共存的原因 17 | println!("new heap start: {:p}", &v[0] as *const i32); 18 | 19 | print_vec("v", v); 20 | } 21 | 22 | fn extend_vec(v: &mut Vec) { 23 | // Vec 堆内存里 T 的个数是指数增长的,我们让它恰好 push 33 个元素 24 | // capacity 会变成 64 25 | (2..34).into_iter().for_each(|i| v.push(i)); 26 | } 27 | 28 | fn print_vec(name: &str, data: Vec) { 29 | let p: [usize; 3] = unsafe { mem::transmute(data) }; 30 | // 打印 Vec 的堆地址,capacity,len 31 | println!("{}: 0x{:x}, {}, {}", name, p[0], p[1], p[2]); 32 | } 33 | -------------------------------------------------------------------------------- /09_multi_owner/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "multi_owner" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [[bin]] 7 | name = "dag" 8 | path = "src/dag.rs" 9 | 10 | [[bin]] 11 | name = "refcell" 12 | path = "src/refcell.rs" 13 | 14 | [[bin]] 15 | name = "dag_mut" 16 | path = "src/dag_mut.rs" 17 | 18 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 19 | 20 | [dependencies] 21 | -------------------------------------------------------------------------------- /09_multi_owner/src/dag.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | 3 | #[allow(dead_code)] 4 | #[derive(Debug)] 5 | struct Node { 6 | id: usize, 7 | downstream: Option>, 8 | } 9 | 10 | impl Node { 11 | pub fn new(id: usize) -> Self { 12 | Self { 13 | id, 14 | downstream: None, 15 | } 16 | } 17 | 18 | pub fn update_downstream(&mut self, downstream: Rc) { 19 | self.downstream = Some(downstream); 20 | } 21 | 22 | pub fn get_downstream(&self) -> Option> { 23 | self.downstream.as_ref().cloned() 24 | } 25 | } 26 | 27 | fn main() { 28 | let mut node1 = Node::new(1); 29 | let mut node2 = Node::new(2); 30 | let mut node3 = Node::new(3); 31 | let node4 = Node::new(4); 32 | node3.update_downstream(Rc::new(node4)); 33 | 34 | node1.update_downstream(Rc::new(node3)); 35 | node2.update_downstream(node1.get_downstream().unwrap()); 36 | println!("node1: {:?}, node2: {:?}", node1, node2); 37 | 38 | // 无法编译通过 39 | // let node5 = Node::new(5); 40 | // let node3 = node1.get_downstream().unwrap(); 41 | // node3.update_downstream(Rc::new(node5)); 42 | 43 | // println!("node1: {:?}, node2: {:?}", node1, node2); 44 | } 45 | -------------------------------------------------------------------------------- /09_multi_owner/src/refcell.rs: -------------------------------------------------------------------------------- 1 | use std::cell::RefCell; 2 | 3 | fn main() { 4 | let data = RefCell::new(1); 5 | { 6 | // 获得 RefCell 内部数据的可变借用 7 | let mut v = data.borrow_mut(); 8 | *v += 1; 9 | } 10 | println!("data: {:?}", data.borrow()); 11 | } 12 | -------------------------------------------------------------------------------- /10_lifetime/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lifetime" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # 如果尝试编译 lifetime 会因为生命周期出错 7 | # [[bin]] 8 | # name = "lifetime" 9 | # path = "src/lifetime.rs" 10 | 11 | [[bin]] 12 | name = "lifetime1" 13 | path = "src/lifetime1.rs" 14 | 15 | # 如果尝试编译 lifetime2 会因为生命周期出错 16 | # [[bin]] 17 | # name = "lifetime2" 18 | # path = "src/lifetime2.rs" 19 | 20 | [[bin]] 21 | name = "lifetime3" 22 | path = "src/lifetime3.rs" 23 | 24 | [[bin]] 25 | name = "lifetime4" 26 | path = "src/lifetime4.rs" 27 | 28 | [[bin]] 29 | name = "lifetime_non_lexical" 30 | path = "src/lifetime_non_lexical.rs" 31 | 32 | [[bin]] 33 | name = "strtok" 34 | path = "src/strtok.rs" 35 | 36 | [[bin]] 37 | name = "lifetime_mono" 38 | path = "src/lifetime_mono.rs" 39 | 40 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 41 | 42 | [dependencies] 43 | -------------------------------------------------------------------------------- /10_lifetime/src/lifetime.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let s1 = String::from("Lindsey"); 3 | let s2 = String::from("Rosie"); 4 | 5 | let result = max(&s1, &s2); 6 | 7 | println!("bigger one: {}", result); 8 | 9 | let result = get_max(s1); 10 | println!("bigger one: {}", result); 11 | } 12 | 13 | fn get_max(s1: &str) -> &str { 14 | max(s1, "Cynthia") 15 | } 16 | 17 | // 这段代码无法编译通过 18 | fn max(s1: &str, s2: &str) -> &str { 19 | if s1 > s2 { 20 | s1 21 | } else { 22 | s2 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /10_lifetime/src/lifetime1.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let s1 = String::from("Lindsey"); 3 | let s2 = String::from("Rosie"); 4 | 5 | let result = max(&s1, &s2); 6 | 7 | println!("bigger one: {}", result); 8 | 9 | let result = get_max(&s1); 10 | println!("bigger one: {}", result); 11 | } 12 | 13 | fn get_max(s1: &str) -> &str { 14 | max(s1, "Cynthia") 15 | } 16 | 17 | fn max<'a>(s1: &'a str, s2: &'a str) -> &'a str { 18 | if s1 > s2 { 19 | s1 20 | } else { 21 | s2 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /10_lifetime/src/lifetime2.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let s1 = String::from("Lindsey"); 3 | let result; 4 | { 5 | let s2 = String::from("Rosie"); 6 | // s2 生命周期不够长 7 | result = max(&s1, &s2); 8 | } 9 | 10 | println!("bigger one: {}", result); 11 | } 12 | 13 | fn max<'a>(s1: &'a str, s2: &'a str) -> &'a str { 14 | if s1 > s2 { 15 | s1 16 | } else { 17 | s2 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /10_lifetime/src/lifetime3.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let s1 = "Lindsey"; 3 | let s2 = String::from("Rosie"); 4 | 5 | let result = max(s1, &s2); 6 | 7 | println!("bigger one: {}", result); 8 | } 9 | 10 | fn max<'a>(s1: &'a str, s2: &'a str) -> &'a str { 11 | if s1 > s2 { 12 | s1 13 | } else { 14 | s2 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /10_lifetime/src/lifetime4.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let s1 = "Hello world"; 3 | 4 | println!("first word of s1: {}", first(s1)); 5 | } 6 | 7 | // 如果你用 clippy,多余的 lifetime 会提醒你不需要 8 | // fn first<'a>(s: &'a str) -> &'a str { 9 | fn first(s: &str) -> &str { 10 | let trimmed = s.trim(); 11 | match trimmed.find(' ') { 12 | None => "", 13 | Some(pos) => &trimmed[..pos], 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /10_lifetime/src/lifetime_mono.rs: -------------------------------------------------------------------------------- 1 | trait Print { 2 | fn print(self); 3 | } 4 | 5 | // lifetime 也不能有不同的实现 6 | // impl<'a> Print for &'a str { 7 | // fn print(self) { 8 | // println!("Arbitrary str: {}", self); 9 | // } 10 | // } 11 | 12 | impl Print for &'static str { 13 | fn print(self) { 14 | println!("'static str: {}", self); 15 | } 16 | } 17 | 18 | // lifetime 不会单体化,所以这个不工作 19 | // fn print_str<'a>(s: &'a str) { 20 | // s.print() 21 | // } 22 | 23 | fn main() { 24 | let s = "hello, world!"; 25 | s.print(); 26 | // print_str(s); 27 | } 28 | -------------------------------------------------------------------------------- /10_lifetime/src/lifetime_non_lexical.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | fn main() { 4 | let mut map = HashMap::new(); 5 | map.insert("hello", "world"); 6 | let key = "hello1"; 7 | 8 | // 按照之前的说法,这段代码无法编译通过,因为同一个 scope 下不能有两个可变引用 9 | // 但因为 RFC2094 non-lexical lifetimes,Rust 编译器可以处理这个场景, 10 | // 因为当 None 时,map.get_mut() 的引用实际已经结束 11 | match map.get_mut(key) /* <----- 可变引用的生命周期一直持续到 match 结果 */ { 12 | Some(v) => do_something(v), 13 | None => { 14 | map.insert(key, "tyr"); // <--- 这里又获得了一个可变引用 15 | } 16 | } 17 | 18 | let s = std::sync::Arc::new(String::from("hello world")); 19 | println!("{:?}", s); 20 | } 21 | 22 | fn do_something(_v: &mut &str) { 23 | todo!() 24 | } 25 | -------------------------------------------------------------------------------- /10_lifetime/src/strtok.rs: -------------------------------------------------------------------------------- 1 | pub fn strtok<'a>(s: &mut &'a str, delimiter: char) -> &'a str { 2 | if let Some(i) = s.find(delimiter) { 3 | let prefix = &s[..i]; 4 | let suffix = &s[(i + delimiter.len_utf8())..]; 5 | *s = suffix; 6 | prefix 7 | } else { 8 | let prefix = *s; 9 | *s = ""; 10 | prefix 11 | } 12 | } 13 | 14 | fn main() { 15 | let s = "hello world".to_owned(); 16 | let mut s1 = s.as_str(); 17 | let hello = strtok(&mut s1, ' '); 18 | println!("hello is: {}, s1: {}, s: {}", hello, s1, s); 19 | } 20 | -------------------------------------------------------------------------------- /11_memory/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "memory" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /11_memory/examples/alignment.rs: -------------------------------------------------------------------------------- 1 | use std::mem::{align_of, size_of}; 2 | 3 | #[allow(dead_code)] 4 | struct S1 { 5 | a: u8, 6 | b: u16, 7 | c: u8, 8 | } 9 | 10 | #[allow(dead_code)] 11 | struct S2 { 12 | a: u8, 13 | c: u8, 14 | b: u16, 15 | } 16 | 17 | fn main() { 18 | println!("sizeof S1: {}, S2: {}", size_of::(), size_of::()); 19 | println!("alignof S1: {}, S2: {}", align_of::(), align_of::()); 20 | } 21 | -------------------------------------------------------------------------------- /11_memory/examples/raii.rs: -------------------------------------------------------------------------------- 1 | use std::fs::File; 2 | use std::io::prelude::*; 3 | fn main() -> std::io::Result<()> { 4 | let mut file = File::create("foo.txt")?; 5 | file.write_all(b"Hello, world!")?; 6 | Ok(()) 7 | } 8 | -------------------------------------------------------------------------------- /11_memory/examples/size.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::mem::size_of; 3 | 4 | #[allow(dead_code)] 5 | enum E { 6 | A(f64), 7 | B(HashMap), 8 | C(Result, String>), 9 | } 10 | 11 | macro_rules! show_size { 12 | (header) => { 13 | println!( 14 | "{:<24} {:>4} {} {}", 15 | "Type", "T", "Option", "Result" 16 | ); 17 | println!("{}", "-".repeat(64)); 18 | }; 19 | ($t:ty) => { 20 | println!( 21 | "{:<24} {:4} {:8} {:12}", 22 | stringify!($t), 23 | size_of::<$t>(), 24 | size_of::>(), 25 | size_of::>(), 26 | ) 27 | }; 28 | } 29 | 30 | fn main() { 31 | show_size!(header); 32 | show_size!(u8); 33 | show_size!(f64); 34 | show_size!(&u8); 35 | show_size!(Box); 36 | show_size!(&[u8]); 37 | 38 | show_size!(String); 39 | show_size!(Vec); 40 | show_size!(HashMap); 41 | show_size!(E); 42 | } 43 | -------------------------------------------------------------------------------- /11_memory/misc/test.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | struct S1 { 5 | u_int8_t a; 6 | u_int16_t b; 7 | u_int8_t c; 8 | }; 9 | 10 | struct S2 { 11 | u_int8_t a; 12 | u_int8_t c; 13 | u_int16_t b; 14 | }; 15 | 16 | void main() { 17 | printf("size of S1: %d, S2: %d", sizeof(struct S1), sizeof(struct S2)); 18 | } 19 | -------------------------------------------------------------------------------- /11_memory/src/lib.rs: -------------------------------------------------------------------------------- 1 | // do nothing 2 | -------------------------------------------------------------------------------- /12_type_system/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "type_system" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [[bin]] 7 | name = "constant" 8 | path = "src/constant.rs" 9 | 10 | [[bin]] 11 | name = "id" 12 | path = "src/id.rs" 13 | 14 | [[bin]] 15 | name = "reader" 16 | path = "src/reader.rs" 17 | 18 | [[bin]] 19 | name = "writer" 20 | path = "src/writer.rs" 21 | 22 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 23 | 24 | [dependencies] 25 | -------------------------------------------------------------------------------- /12_type_system/src/constant.rs: -------------------------------------------------------------------------------- 1 | const PI: f64 = std::f64::consts::PI; 2 | static E: f32 = std::f32::consts::E; 3 | 4 | fn main() { 5 | const V: u32 = 10; 6 | static V1: &str = "hello"; 7 | println!("PI: {}, E: {}, V {}, V1: {}", PI, E, V, V1); 8 | } 9 | -------------------------------------------------------------------------------- /12_type_system/src/id.rs: -------------------------------------------------------------------------------- 1 | fn id(x: T) -> T { 2 | x 3 | } 4 | 5 | fn main() { 6 | let int = id(42); 7 | let string = id("Tyr"); 8 | println!("{}, {}", int, string); 9 | } 10 | -------------------------------------------------------------------------------- /12_type_system/src/reader.rs: -------------------------------------------------------------------------------- 1 | use std::fs::File; 2 | use std::io::{BufReader, Read, Result}; 3 | 4 | struct MyReader { 5 | reader: R, 6 | buf: String, 7 | } 8 | 9 | impl MyReader { 10 | pub fn new(reader: R) -> Self { 11 | Self { 12 | reader, 13 | buf: String::with_capacity(1024), 14 | } 15 | } 16 | } 17 | 18 | impl MyReader 19 | where 20 | R: Read, 21 | { 22 | pub fn process(&mut self) -> Result { 23 | self.reader.read_to_string(&mut self.buf) 24 | } 25 | } 26 | 27 | fn main() { 28 | let f = File::open("/etc/hosts").unwrap(); 29 | let mut reader = MyReader::new(BufReader::new(f)); 30 | 31 | let size = reader.process().unwrap(); 32 | println!("total size read: {}", size); 33 | } 34 | -------------------------------------------------------------------------------- /12_type_system/src/writer.rs: -------------------------------------------------------------------------------- 1 | use std::io::{BufWriter, Write}; 2 | use std::net::TcpStream; 3 | 4 | #[derive(Debug)] 5 | struct MyWriter { 6 | writer: W, 7 | } 8 | 9 | impl MyWriter { 10 | pub fn new(writer: W) -> Self { 11 | Self { writer } 12 | } 13 | 14 | pub fn write(&mut self, buf: &str) -> std::io::Result<()> { 15 | self.writer.write_all(buf.as_bytes()) 16 | } 17 | } 18 | 19 | fn main() { 20 | let stream = TcpStream::connect("127.0.0.1:8080").unwrap(); 21 | 22 | let mut writer = MyWriter::new(BufWriter::new(stream)); 23 | writer.write("hello world!").unwrap(); 24 | } 25 | -------------------------------------------------------------------------------- /13_traits/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "traits" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [[bin]] 7 | name = "write" 8 | path = "src/write.rs" 9 | 10 | [[bin]] 11 | name = "parse" 12 | path = "src/parse.rs" 13 | 14 | [[bin]] 15 | name = "parse1" 16 | path = "src/parse1.rs" 17 | 18 | [[bin]] 19 | name = "parse2" 20 | path = "src/parse2.rs" 21 | 22 | [[bin]] 23 | name = "iterator" 24 | path = "src/iterator.rs" 25 | 26 | [[bin]] 27 | name = "complex" 28 | path = "src/complex.rs" 29 | 30 | [[bin]] 31 | name = "animal" 32 | path = "src/animal.rs" 33 | 34 | [[bin]] 35 | name = "formatter" 36 | path = "src/formatter.rs" 37 | 38 | [[bin]] 39 | name = "dd" 40 | path = "src/dd.rs" 41 | 42 | [[bin]] 43 | name = "trait_object" 44 | path = "src/trait_object.rs" 45 | 46 | [[bin]] 47 | name = "trait_object_internal" 48 | path = "src/trait_object_internal.rs" 49 | 50 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 51 | 52 | [dependencies] 53 | regex = "1.5" 54 | -------------------------------------------------------------------------------- /13_traits/src/animal.rs: -------------------------------------------------------------------------------- 1 | struct Cat; 2 | struct Dog; 3 | 4 | trait Animal { 5 | fn name(&self) -> &'static str; 6 | } 7 | 8 | impl Animal for Cat { 9 | fn name(&self) -> &'static str { 10 | "Cat" 11 | } 12 | } 13 | 14 | impl Animal for Dog { 15 | fn name(&self) -> &'static str { 16 | "Dog" 17 | } 18 | } 19 | 20 | fn name(animal: impl Animal) -> &'static str { 21 | animal.name() 22 | } 23 | 24 | fn main() { 25 | let cat = Cat; 26 | println!("cat: {}", name(cat)); 27 | } 28 | -------------------------------------------------------------------------------- /13_traits/src/dd.rs: -------------------------------------------------------------------------------- 1 | pub trait Fly { 2 | fn fly(&self); 3 | } 4 | 5 | struct Goose; 6 | 7 | #[allow(dead_code)] 8 | struct Duck { 9 | height: u8, 10 | } 11 | 12 | impl Goose { 13 | pub fn new() -> Self { 14 | Self 15 | } 16 | } 17 | 18 | impl Duck { 19 | pub fn new(height: u8) -> Self { 20 | Self { height } 21 | } 22 | } 23 | 24 | impl Fly for Goose { 25 | fn fly(&self) { 26 | println!("Goose is flying"); 27 | } 28 | } 29 | 30 | impl Fly for Duck { 31 | fn fly(&self) { 32 | println!("Duck is flying"); 33 | } 34 | } 35 | 36 | fn fly(a: impl Fly) { 37 | a.fly(); 38 | } 39 | 40 | // impl Fly 作为返回值,需要有某个确定的类型,这样才能编译通过 41 | // 这段代码无法提供确定的类型,所以出错 42 | // fn select(name: &str) -> impl Fly { 43 | // match name { 44 | // "goose" => Goose::new() as Fly, 45 | // "duck" => Duck::new(3) as Fly, 46 | // } 47 | // } 48 | 49 | fn main() { 50 | let g = Goose::new(); 51 | let d = Duck::new(3); 52 | fly(g); 53 | fly(d); 54 | } 55 | -------------------------------------------------------------------------------- /13_traits/src/parse.rs: -------------------------------------------------------------------------------- 1 | use regex::Regex; 2 | pub trait Parse { 3 | fn parse(s: &str) -> Self; 4 | } 5 | 6 | impl Parse for u8 { 7 | fn parse(s: &str) -> Self { 8 | let re: Regex = Regex::new(r"^[0-9]+").unwrap(); 9 | if let Some(captures) = re.captures(s) { 10 | captures 11 | .get(0) 12 | .map_or(0, |s| s.as_str().parse().unwrap_or(0)) 13 | } else { 14 | 0 15 | } 16 | } 17 | } 18 | 19 | #[test] 20 | fn parse_should_work() { 21 | assert_eq!(u8::parse("123abcd"), 123); 22 | assert_eq!(u8::parse("1234abcd"), 0); 23 | assert_eq!(u8::parse("abcd"), 0); 24 | } 25 | 26 | fn main() { 27 | println!("result: {}", u8::parse("255 hello world")); 28 | } 29 | -------------------------------------------------------------------------------- /13_traits/src/parse1.rs: -------------------------------------------------------------------------------- 1 | use std::str::FromStr; 2 | 3 | use regex::Regex; 4 | pub trait Parse { 5 | fn parse(s: &str) -> Self; 6 | } 7 | 8 | impl Parse for T 9 | where 10 | T: FromStr + Default, 11 | { 12 | fn parse(s: &str) -> Self { 13 | let re: Regex = Regex::new(r"^[0-9]+(\.[0-9]+)?").unwrap(); 14 | let d = || Default::default(); 15 | if let Some(captures) = re.captures(s) { 16 | captures 17 | .get(0) 18 | .map_or(d(), |s| s.as_str().parse().unwrap_or_else(|_| d())) 19 | } else { 20 | d() 21 | } 22 | } 23 | } 24 | 25 | #[test] 26 | fn parse_should_work() { 27 | assert_eq!(u32::parse("123abcd"), 123); 28 | assert_eq!(u32::parse("123.45abcd"), 0); 29 | assert_eq!(f64::parse("123.45abcd").to_string(), "123.45"); 30 | assert_eq!(f64::parse("abcd").to_string(), "0"); 31 | } 32 | 33 | fn main() { 34 | println!("result: {}", u8::parse("255 hello world")); 35 | } 36 | -------------------------------------------------------------------------------- /13_traits/src/trait_object.rs: -------------------------------------------------------------------------------- 1 | use std::{fs::File, io::Write}; 2 | fn main() { 3 | let mut f = File::create("/tmp/test_write_trait").unwrap(); 4 | let w: &mut dyn Write = &mut f; 5 | w.write_all(b"hello ").unwrap(); 6 | 7 | // 无法在 trait object 里调用使用了 Self 的函数 8 | // let w1 = w.by_ref(); 9 | // w1.write_all(b"world").unwrap(); 10 | } 11 | -------------------------------------------------------------------------------- /13_traits/src/write.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::io::Write; 3 | 4 | struct BufBuilder { 5 | buf: Vec, 6 | } 7 | 8 | impl BufBuilder { 9 | pub fn new() -> Self { 10 | Self { 11 | buf: Vec::with_capacity(1024), 12 | } 13 | } 14 | } 15 | 16 | impl fmt::Debug for BufBuilder { 17 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 18 | write!(f, "{}", String::from_utf8_lossy(self.buf.as_ref())) 19 | } 20 | } 21 | 22 | impl Write for BufBuilder { 23 | fn write(&mut self, buf: &[u8]) -> std::io::Result { 24 | self.buf.extend_from_slice(buf); 25 | Ok(buf.len()) 26 | } 27 | 28 | fn flush(&mut self) -> std::io::Result<()> { 29 | Ok(()) 30 | } 31 | } 32 | 33 | fn main() { 34 | let mut buf = BufBuilder::new(); 35 | buf.write_all(b"Hello world!").unwrap(); 36 | println!("{:?}", buf); 37 | } 38 | -------------------------------------------------------------------------------- /14_sys_traits/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "yaml.customTags": [ 3 | "!And", 4 | "!And sequence", 5 | "!If", 6 | "!If sequence", 7 | "!Not", 8 | "!Not sequence", 9 | "!Equals", 10 | "!Equals sequence", 11 | "!Or", 12 | "!Or sequence", 13 | "!FindInMap", 14 | "!FindInMap sequence", 15 | "!Base64", 16 | "!Join", 17 | "!Join sequence", 18 | "!Cidr", 19 | "!Ref", 20 | "!Sub", 21 | "!Sub sequence", 22 | "!GetAtt", 23 | "!GetAZs", 24 | "!ImportValue", 25 | "!ImportValue sequence", 26 | "!Select", 27 | "!Select sequence", 28 | "!Split", 29 | "!Split sequence" 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /14_sys_traits/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sys_traits" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [[bin]] 7 | name = "dev" 8 | path = "src/dev.rs" 9 | 10 | [[bin]] 11 | name = "copy_drop" 12 | path = "src/copy_drop.rs" 13 | 14 | [[bin]] 15 | name = "sized" 16 | path = "src/sized.rs" 17 | 18 | [[bin]] 19 | name = "send_sync" 20 | path = "src/send_sync.rs" 21 | 22 | [[bin]] 23 | name = "convert" 24 | path = "src/convert.rs" 25 | 26 | [[bin]] 27 | name = "deref" 28 | path = "src/deref.rs" 29 | 30 | [[bin]] 31 | name = "default" 32 | path = "src/default.rs" 33 | 34 | [[bin]] 35 | name = "linked_list" 36 | path = "src/linked_list.rs" 37 | 38 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 39 | 40 | [dependencies] 41 | -------------------------------------------------------------------------------- /14_sys_traits/src/deref.rs: -------------------------------------------------------------------------------- 1 | use std::ops::{Deref, DerefMut}; 2 | 3 | #[derive(Debug)] 4 | struct Buffer(Vec); 5 | 6 | impl Buffer { 7 | pub fn new(v: impl Into>) -> Self { 8 | Self(v.into()) 9 | } 10 | } 11 | 12 | impl Deref for Buffer { 13 | type Target = [T]; 14 | 15 | fn deref(&self) -> &Self::Target { 16 | &self.0 17 | } 18 | } 19 | 20 | impl DerefMut for Buffer { 21 | fn deref_mut(&mut self) -> &mut Self::Target { 22 | &mut self.0 23 | } 24 | } 25 | 26 | fn main() { 27 | let mut buf = Buffer::new([1, 3, 2, 4]); 28 | // 因为实现了 Deref 和 DerefMut,这里 buf 可以直接访问 Vec 的方法 29 | // 下面这句相当于:(*buf).sort_unstable(),也就是 (*&mut buf.0).sort_unstable() 30 | buf.sort_unstable(); 31 | println!("buf: {:?}", buf); 32 | } 33 | -------------------------------------------------------------------------------- /14_sys_traits/src/dev.rs: -------------------------------------------------------------------------------- 1 | #[allow(dead_code)] 2 | #[derive(Clone, Debug)] 3 | struct Developer { 4 | name: String, 5 | age: u8, 6 | lang: Language, 7 | } 8 | 9 | #[allow(dead_code)] 10 | #[derive(Clone, Copy, Debug)] 11 | enum Language { 12 | Rust, 13 | TypeScript, 14 | Elixir, 15 | Haskell, 16 | } 17 | 18 | fn main() { 19 | let dev = Developer { 20 | name: "Tyr".to_string(), 21 | age: 18, 22 | lang: Language::Rust, 23 | }; 24 | let dev1 = dev.clone(); 25 | println!("dev: {:?}, addr of name: {:p}", dev, dev.name.as_str()); 26 | println!("dev1: {:?}, addr of name: {:p}", dev1, dev1.name.as_str()); 27 | } 28 | -------------------------------------------------------------------------------- /14_sys_traits/src/sized.rs: -------------------------------------------------------------------------------- 1 | #[allow(dead_code)] 2 | struct Data { 3 | inner: T, 4 | } 5 | 6 | fn process_data(_data: Data) { 7 | todo!(); 8 | } 9 | 10 | #[allow(dead_code)] 11 | struct UnsizedData { 12 | inner: T, 13 | } 14 | 15 | // 无法编译通过,函数的参数必须是编译时大小确定的 16 | // fn process_unsized_data(data: UnsizedData) 17 | // where 18 | // T: ?Sized, 19 | // { 20 | // todo!(); 21 | // } 22 | 23 | #[allow(unused_variables)] 24 | fn main() { 25 | let v = Data { inner: 0 }; 26 | process_data(v); 27 | let v = UnsizedData { inner: 0 }; 28 | // process_unsized_data(v); 29 | } 30 | -------------------------------------------------------------------------------- /15_smart_pointers/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "smart_pointers" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [[bin]] 7 | name = "box" 8 | path = "src/box.rs" 9 | 10 | [[bin]] 11 | name = "allocator" 12 | path = "src/allocator.rs" 13 | 14 | [[bin]] 15 | name = "borrow" 16 | path = "src/borrow.rs" 17 | 18 | [[bin]] 19 | name = "cow1" 20 | path = "src/cow1.rs" 21 | 22 | [[bin]] 23 | name = "cow2" 24 | path = "src/cow2.rs" 25 | 26 | [[bin]] 27 | name = "guard" 28 | path = "src/guard.rs" 29 | 30 | [[bin]] 31 | name = "guard1" 32 | path = "src/guard1.rs" 33 | 34 | [[bin]] 35 | name = "mystring" 36 | path = "src/mystring.rs" 37 | 38 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 39 | 40 | [dependencies] 41 | url = "2" 42 | serde = { version = "1",features = ["derive"] } 43 | serde_json = "1" 44 | rayon = "1" 45 | lazy_static = "1" 46 | -------------------------------------------------------------------------------- /15_smart_pointers/src/allocator.rs: -------------------------------------------------------------------------------- 1 | use std::alloc::{GlobalAlloc, Layout, System}; 2 | 3 | struct MyAllocator; 4 | 5 | unsafe impl GlobalAlloc for MyAllocator { 6 | unsafe fn alloc(&self, layout: Layout) -> *mut u8 { 7 | let data = System.alloc(layout); 8 | eprintln!("ALLOC: {:p}, size {}", data, layout.size()); 9 | data 10 | } 11 | 12 | unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { 13 | System.dealloc(ptr, layout); 14 | eprintln!("FREE: {:p}, size {}", ptr, layout.size()); 15 | } 16 | } 17 | 18 | #[global_allocator] 19 | static GLOBAL: MyAllocator = MyAllocator; 20 | 21 | #[allow(dead_code)] 22 | struct Matrix { 23 | // 使用不规则的数字如 505 可以让 dbg! 的打印很容易分辨出来 24 | data: [u8; 505], 25 | } 26 | 27 | impl Default for Matrix { 28 | fn default() -> Self { 29 | Self { data: [0; 505] } 30 | } 31 | } 32 | 33 | fn main() { 34 | // 在这句执行之前已经有一些内存分配和释放 35 | let data = Box::new(Matrix::default()); 36 | println!( 37 | "!!! allocated memory: {:p}, len: {}", 38 | &*data, 39 | std::mem::size_of::() 40 | ); 41 | 42 | // data 在这里 drop,可以在打印中看到 FREE 43 | // 之后还有很多其它内存被释放 44 | } 45 | -------------------------------------------------------------------------------- /15_smart_pointers/src/borrow.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Borrow; 2 | 3 | fn main() { 4 | let s = "hello world!".to_owned(); 5 | 6 | // 这里必须声明类型,因为 String 有多个 Borrow 实现 7 | // 借用为 &String 8 | let r1: &String = s.borrow(); 9 | // 借用为 &str 10 | let r2: &str = s.borrow(); 11 | 12 | println!("r1: {:p}, r2: {:p}", r1, r2); 13 | } 14 | -------------------------------------------------------------------------------- /15_smart_pointers/src/box.rs: -------------------------------------------------------------------------------- 1 | // #![feature(dropck_eyepatch)] 2 | 3 | // struct MyBox(Box); 4 | 5 | // unsafe impl<#[may_dangle] T> Drop for MyBox { 6 | // fn drop(&mut self) { 7 | // todo!(); 8 | // } 9 | // } 10 | 11 | fn main() { 12 | // 在堆上分配 16M 内存,但它会现在栈上出现,再移动到堆上 13 | let boxed = Box::new([0u8; 1 << 24]); 14 | println!("len: {}", boxed.len()); 15 | } 16 | -------------------------------------------------------------------------------- /15_smart_pointers/src/cow1.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | 3 | use url::Url; 4 | fn main() { 5 | let url = Url::parse("https://tyr.com/rust?page=1024&sort=desc&extra=hello%20world").unwrap(); 6 | let mut pairs = url.query_pairs(); 7 | 8 | assert_eq!(pairs.count(), 3); 9 | 10 | let (mut k, v) = pairs.next().unwrap(); 11 | // 因为 k, v 都是 Cow 他们用起来感觉和 &str 或者 String 一样 12 | // 此刻,他们都是 Borrowed 13 | println!("key: {}, v: {}", k, v); 14 | // 当修改发生时,k 变成 Owned 15 | k.to_mut().push_str("_lala"); 16 | 17 | print_pairs((k, v)); 18 | 19 | print_pairs(pairs.next().unwrap()); 20 | print_pairs(pairs.next().unwrap()); 21 | } 22 | 23 | fn print_pairs(pair: (Cow, Cow)) { 24 | println!("key: {}, value: {}", show_cow(pair.0), show_cow(pair.1)); 25 | } 26 | 27 | fn show_cow(cow: Cow) -> String { 28 | match cow { 29 | Cow::Borrowed(v) => format!("Borrowed {}", v), 30 | Cow::Owned(v) => format!("Owned {}", v), 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /15_smart_pointers/src/cow2.rs: -------------------------------------------------------------------------------- 1 | use serde::Deserialize; 2 | use std::borrow::Cow; 3 | 4 | #[allow(dead_code)] 5 | #[derive(Debug, Deserialize)] 6 | struct User<'input> { 7 | #[serde(borrow)] 8 | name: Cow<'input, str>, 9 | age: u8, 10 | } 11 | 12 | fn main() { 13 | let input = r#"{ "name": "Tyr", "age": 18 }"#; 14 | let user: User = serde_json::from_str(input).unwrap(); 15 | 16 | match user.name { 17 | Cow::Borrowed(x) => println!("borrowed {}", x), 18 | Cow::Owned(x) => println!("owned {}", x), 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /15_smart_pointers/src/guard1.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Mutex; 2 | 3 | fn main() { 4 | // 5 | let m = Mutex::new(Mutex::new(1)); 6 | let g = m.lock().unwrap(); 7 | { 8 | rayon::join( 9 | || { 10 | let mut g1 = g.lock().unwrap(); 11 | *g1 += 1; 12 | println!("Thread 1: {:?}", *g1); 13 | }, 14 | || { 15 | let mut g1 = g.lock().unwrap(); 16 | *g1 += 1; 17 | println!("Thread 1: {:?}", *g1); 18 | }, 19 | ); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /16_data_structure/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "data-structure" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [[bin]] 7 | name = "type_name" 8 | path = "src/type_name.rs" 9 | 10 | [[bin]] 11 | name = "slice1" 12 | path = "src/slice1.rs" 13 | 14 | [[bin]] 15 | name = "slice2" 16 | path = "src/slice2.rs" 17 | 18 | [[bin]] 19 | name = "slice3" 20 | path = "src/slice3.rs" 21 | 22 | [[bin]] 23 | name = "slice4" 24 | path = "src/slice4.rs" 25 | 26 | [[bin]] 27 | name = "slice5" 28 | path = "src/slice5.rs" 29 | 30 | [[bin]] 31 | name = "boxed" 32 | path = "src/boxed.rs" 33 | 34 | [[bin]] 35 | name = "iter" 36 | path = "src/iter.rs" 37 | 38 | [[bin]] 39 | name = "iter_ext" 40 | path = "src/iter_ext.rs" 41 | 42 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 43 | 44 | [dependencies] 45 | itertools = "0.10" 46 | -------------------------------------------------------------------------------- /16_data_structure/src/boxed.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Deref; 2 | 3 | fn main() { 4 | let mut v1 = vec![1, 2, 3, 4]; 5 | v1.push(5); 6 | println!("cap should be 8: {}", v1.capacity()); 7 | 8 | // 从 Vec 转换成 Box<[T]>,此时会丢弃多余的 capacity 9 | let b1 = v1.into_boxed_slice(); 10 | let mut b2 = b1.clone(); 11 | 12 | let v2 = b1.into_vec(); 13 | println!("cap should be exactly 5: {}", v2.capacity()); 14 | 15 | assert!(b2.deref() == v2); 16 | 17 | // Box<[T]> 可以更改其内部数据,但无法 push 18 | b2[0] = 2; 19 | // b2.push(6); 20 | println!("b2: {:?}", b2); 21 | 22 | // 注意 Box<[T]> 和 Box<[T; n]> 并不相同 23 | let b3 = Box::new([2, 2, 3, 4, 5]); 24 | println!("b3: {:?}", b3); 25 | 26 | // b2 和 b3 相等,但 b3.deref() 和 v2 无法比较 27 | assert!(b2 == b3); 28 | // assert!(b3.deref() == v2); 29 | } 30 | -------------------------------------------------------------------------------- /16_data_structure/src/iter.rs: -------------------------------------------------------------------------------- 1 | use itertools::Itertools; 2 | 3 | fn main() { 4 | let err_str = "bad happened"; 5 | let input = vec![Ok(21), Err(err_str), Ok(7)]; 6 | let it = input 7 | .into_iter() 8 | .filter_map_ok(|i| if i > 10 { Some(i * 2) } else { None }); 9 | // 结果应该是:vec![Ok(42), Err(err_str)] 10 | println!("{:?}", it.collect::>()); 11 | } 12 | -------------------------------------------------------------------------------- /16_data_structure/src/iter_ext.rs: -------------------------------------------------------------------------------- 1 | // IteratorExt 「继承」Iterator,这样可以使用 Iterator 的全部功能 2 | pub trait IteratorExt: Iterator { 3 | fn window_count(self, count: u32) -> WindowCount 4 | where 5 | Self: Sized, 6 | { 7 | WindowCount { iter: self, count } 8 | } 9 | } 10 | 11 | // 这句很重要,它让所有实现了 Iterator 的 T 都自动实现 IteratorExt 12 | impl IteratorExt for T where T: Iterator {} 13 | 14 | pub struct WindowCount { 15 | pub(crate) iter: I, 16 | count: u32, 17 | } 18 | 19 | impl Iterator for WindowCount { 20 | type Item = Vec; 21 | 22 | fn next(&mut self) -> Option { 23 | let data = (0..self.count) 24 | .filter_map(|_| self.iter.next()) 25 | .collect::>(); 26 | if data.is_empty() { 27 | None 28 | } else { 29 | Some(data) 30 | } 31 | } 32 | } 33 | 34 | fn main() { 35 | let data = vec![1, 2, 3, 4, 5]; 36 | let result = data.iter().window_count(2).collect::>>(); 37 | println!("{:?}", result); 38 | } 39 | -------------------------------------------------------------------------------- /16_data_structure/src/slice1.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let arr = [1, 2, 3, 4, 5]; 3 | let vec = vec![1, 2, 3, 4, 5]; 4 | let s1 = &arr[1..3]; 5 | let s2 = &vec[1..3]; 6 | println!("s1: {:?}, s2: {:?}", s1, s2); 7 | 8 | // &[T] 和 &[T] 是否相等取决于长度和内容是否相等 9 | assert_eq!(s1, s2); 10 | // &[T] 可以和 Vec/[T;n] 比较,也会看长度和内容 11 | assert_eq!(&arr[..], vec); 12 | assert_eq!(&vec[..], arr); 13 | } 14 | -------------------------------------------------------------------------------- /16_data_structure/src/slice2.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | fn main() { 3 | let v = vec![1, 2, 3, 4]; 4 | 5 | // Vec 实现了 Deref,&Vec 会被自动解引用为 &[T],符合接口定义 6 | print_slice(&v); 7 | // 直接是 &[T],符合接口定义 8 | print_slice(&v[..]); 9 | 10 | // &Vec 支持 AsRef<[T]> 11 | print_slice1(&v); 12 | // &[T] 支持 AsRef<[T]> 13 | print_slice1(&v[..]); 14 | // Vec 也支持 AsRef<[T]> 15 | print_slice1(v); 16 | 17 | let arr = [1, 2, 3, 4]; 18 | // 数组虽没有实现 Deref,但它的解引用就是 &[T] 19 | print_slice(&arr); 20 | print_slice(&arr[..]); 21 | print_slice1(&arr); 22 | print_slice1(&arr[..]); 23 | print_slice1(arr); 24 | } 25 | 26 | // 注意下面的泛型函数的使用 27 | fn print_slice(s: &[T]) { 28 | println!("{:?}", s); 29 | } 30 | 31 | fn print_slice1(s: T) 32 | where 33 | T: AsRef<[U]>, 34 | U: fmt::Debug, 35 | { 36 | println!("{:?}", s.as_ref()); 37 | } 38 | -------------------------------------------------------------------------------- /16_data_structure/src/slice3.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // 这里 Vec 在调用 iter() 时被解引用成 &[T],所以可以访问 iter() 3 | let result = vec![1, 2, 3, 4] 4 | .iter() 5 | .map(|v| v * v) 6 | .filter(|v| *v < 16) 7 | .take(1) 8 | .collect::>(); 9 | 10 | println!("{:?}", result); 11 | } 12 | -------------------------------------------------------------------------------- /16_data_structure/src/slice4.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | fn main() { 3 | let s = String::from("hello"); 4 | print_slice(&s); 5 | print_slice(&s[..]); 6 | 7 | // String 支持 AsRef 8 | print_slice1(&s); 9 | print_slice1(&s[..]); 10 | print_slice1(s.clone()); 11 | 12 | // String 也实现了 AsRef<[u8]>,所以下面的代码成立 13 | // 打印出来是 [104, 101, 108, 108, 111] 14 | print_slice2(&s); 15 | print_slice2(&s[..]); 16 | print_slice2(s); 17 | } 18 | 19 | fn print_slice(s: &str) { 20 | println!("{:?}", s); 21 | } 22 | 23 | fn print_slice1>(s: T) { 24 | println!("{:?}", s.as_ref()); 25 | } 26 | 27 | fn print_slice2(s: T) 28 | where 29 | T: AsRef<[U]>, 30 | U: fmt::Debug, 31 | { 32 | println!("{:?}", s.as_ref()); 33 | } 34 | 35 | #[allow(dead_code)] 36 | fn print_slice3(s: T) 37 | where 38 | T: AsRef, 39 | U: fmt::Debug, 40 | { 41 | println!("{:?}", s.as_ref()); 42 | } 43 | -------------------------------------------------------------------------------- /16_data_structure/src/slice5.rs: -------------------------------------------------------------------------------- 1 | use std::iter::FromIterator; 2 | 3 | fn main() { 4 | let arr = ['h', 'e', 'l', 'l', 'o']; 5 | let vec = vec!['h', 'e', 'l', 'l', 'o']; 6 | let s = String::from("hello"); 7 | let s1 = &arr[..2]; 8 | let s2 = &vec[..2]; 9 | // &str 本身就是一个特殊的 slice 10 | let s3 = &s[..2]; 11 | println!("s1: {:?}, s2: {:?}, s3: {:?}", s1, s2, s3); 12 | 13 | // &[char] 和 &[char] 是否相等取决于长度和内容是否相等 14 | assert_eq!(s1, s2); 15 | // &[char] 和 &str 不能直接对比,我们把 s3 变成 Vec 16 | assert_eq!(s2, s3.chars().collect::>()); 17 | // &[char] 可以通过迭代器转换成 String,String 和 &str 可以直接对比 18 | assert_eq!(String::from_iter(s2), s3); 19 | } 20 | -------------------------------------------------------------------------------- /16_data_structure/src/type_name.rs: -------------------------------------------------------------------------------- 1 | pub trait TypeName { 2 | fn type_name(&self) -> &'static str; 3 | } 4 | 5 | impl TypeName for T { 6 | fn type_name(&self) -> &'static str { 7 | std::any::type_name::() 8 | } 9 | } 10 | 11 | fn main() { 12 | let s = String::from("hello"); 13 | let s1 = &s; 14 | let s2 = s.as_str(); 15 | let s3 = &s[..]; 16 | println!( 17 | "s: {}, s1: {}, s2: {}, s3: {}", 18 | s.type_name(), 19 | s1.type_name(), 20 | s2.type_name(), 21 | s3.type_name() 22 | ); 23 | 24 | let v = vec![1, 2, 3, 4]; 25 | let v1 = &v; 26 | let v2 = v.as_slice(); 27 | let v3 = &v[..]; 28 | let v4 = v.clone().into_boxed_slice(); 29 | 30 | println!( 31 | "v: {}, v1: {}, v2: {}, v3: {}, v4: {}", 32 | v.type_name(), 33 | v1.type_name(), 34 | v2.type_name(), 35 | v3.type_name(), 36 | v4.type_name() 37 | ); 38 | } 39 | -------------------------------------------------------------------------------- /17_hash_table/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hashtable" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [[bin]] 7 | name = "hashmap1" 8 | path = "src/hashmap1.rs" 9 | 10 | [[bin]] 11 | name = "hashmap2" 12 | path = "src/hashmap2.rs" 13 | 14 | [[bin]] 15 | name = "hash" 16 | path = "src/hash.rs" 17 | 18 | [[bin]] 19 | name = "siphasher" 20 | path = "src/siphasher.rs" 21 | doc = false 22 | 23 | [[bin]] 24 | name = "hashmap3" 25 | path = "src/hashmap3.rs" 26 | 27 | [[bin]] 28 | name = "btreemap1" 29 | path = "src/btreemap1.rs" 30 | 31 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 32 | 33 | [dependencies] 34 | -------------------------------------------------------------------------------- /17_hash_table/src/btreemap1.rs: -------------------------------------------------------------------------------- 1 | use std::collections::BTreeMap; 2 | 3 | fn main() { 4 | let map = BTreeMap::new(); 5 | let mut map = explain("empty", map); 6 | 7 | for i in 0..16usize { 8 | map.insert(format!("Tyr {}", i), i); 9 | } 10 | 11 | let mut map = explain("added", map); 12 | 13 | map.remove("Tyr 1"); 14 | 15 | let map = explain("remove 1", map); 16 | 17 | for item in map.iter() { 18 | println!("{:?}", item); 19 | } 20 | } 21 | 22 | // BTreeMap 结构有 height,node 和 length 23 | // 我们 transmute 打印之后,再 transmute 回去 24 | fn explain(name: &str, map: BTreeMap) -> BTreeMap { 25 | let arr: [usize; 3] = unsafe { std::mem::transmute(map) }; 26 | println!( 27 | "{}: height: {}, root node: 0x{:x}, len: 0x{:x}", 28 | name, arr[0], arr[1], arr[2] 29 | ); 30 | unsafe { std::mem::transmute(arr) } 31 | } 32 | -------------------------------------------------------------------------------- /17_hash_table/src/hash.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | collections::{hash_map::DefaultHasher, HashMap}, 3 | hash::{Hash, Hasher}, 4 | }; 5 | 6 | // 如果要支持 Hash,可以用 #[derive(Hash)],前提是每个字段都实现了 Hash 7 | // 如果要能作为 HashMap 的 key,还需要 PartialEq 和 Eq 8 | #[derive(Debug, Hash, PartialEq, Eq)] 9 | struct Student<'a> { 10 | name: &'a str, 11 | age: u8, 12 | } 13 | 14 | impl<'a> Student<'a> { 15 | pub fn new(name: &'a str, age: u8) -> Self { 16 | Self { name, age } 17 | } 18 | } 19 | fn main() { 20 | let mut hasher = DefaultHasher::new(); 21 | let student = Student::new("Tyr", 18); 22 | // 实现了 Hash 的数据结构可以直接调用 hash 方法 23 | student.hash(&mut hasher); 24 | let mut map = HashMap::new(); 25 | // 实现了 Hash / PartialEq / Eq 的数据结构可以作为 HashMap 的 key 26 | map.insert(student, vec!["Math", "Writing"]); 27 | println!("hash: 0x{:x}, map: {:?}", hasher.finish(), map); 28 | } 29 | -------------------------------------------------------------------------------- /17_hash_table/src/hashmap1.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | fn main() { 4 | let mut map = HashMap::new(); 5 | explain("empty", &map); 6 | 7 | map.insert('a', 1); 8 | explain("added 1", &map); 9 | 10 | map.insert('b', 2); 11 | map.insert('c', 3); 12 | explain("added 3", &map); 13 | 14 | map.insert('d', 4); 15 | explain("added 4", &map); 16 | 17 | // get 时需要使用引用,并且也返回引用 18 | assert_eq!(map.get(&'a'), Some(&1)); 19 | assert_eq!(map.get_key_value(&'b'), Some((&'b', &2))); 20 | 21 | map.remove(&'a'); 22 | // 删除后就找不到了 23 | assert!(!map.contains_key(&'a')); 24 | assert_eq!(map.get(&'a'), None); 25 | explain("removed", &map); 26 | // shrink 后哈希表变小 27 | map.shrink_to_fit(); 28 | explain("shrinked", &map); 29 | } 30 | 31 | fn explain(name: &str, map: &HashMap) { 32 | println!("{}: len: {}, cap: {}", name, map.len(), map.capacity()); 33 | } 34 | -------------------------------------------------------------------------------- /17_hash_table/src/hashmap2.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | fn main() { 4 | let map = HashMap::new(); 5 | let mut map = explain("empty", map); 6 | 7 | map.insert('a', 1); 8 | let mut map = explain("added 1", map); 9 | map.insert('b', 2); 10 | map.insert('c', 3); 11 | 12 | let mut map = explain("added 3", map); 13 | 14 | map.insert('d', 4); 15 | 16 | let mut map = explain("added 4", map); 17 | 18 | map.remove(&'a'); 19 | 20 | explain("final", map); 21 | } 22 | 23 | // HashMap 结构有两个 u64 的 RandomState,然后是四个 usize, 24 | // 分别是 bucket_mask, ctrl, growth_left 和 items 25 | // 我们 transmute 打印之后,再 transmute 回去 26 | fn explain(name: &str, map: HashMap) -> HashMap { 27 | let arr: [usize; 6] = unsafe { std::mem::transmute(map) }; 28 | println!( 29 | "{}: bucket_mask 0x{:x}, ctrl 0x{:x}, growth_left: {}, items: {}", 30 | name, arr[2], arr[3], arr[4], arr[5] 31 | ); 32 | unsafe { std::mem::transmute(arr) } 33 | } 34 | -------------------------------------------------------------------------------- /17_hash_table/src/hashmap3.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | fn main() { 4 | let map = HashMap::new(); 5 | let mut map = explain("empty", map); 6 | 7 | map.insert(String::from("a"), 1); 8 | let mut map = explain("added 1", map); 9 | map.insert(String::from("b"), 2); 10 | map.insert(String::from("c"), 3); 11 | 12 | let mut map = explain("added 3", map); 13 | 14 | map.insert(String::from("d"), 4); 15 | 16 | let mut map = explain("added 4", map); 17 | 18 | map.remove("a"); 19 | 20 | explain("final", map); 21 | } 22 | 23 | // HashMap 结构有两个 u64 的 RandomState,然后是四个 usize, 24 | // 分别是 bucket_mask, ctrl, growth_left 和 items 25 | // 我们 transmute 打印之后,再 transmute 回去 26 | fn explain(name: &str, map: HashMap) -> HashMap { 27 | let arr: [usize; 6] = unsafe { std::mem::transmute(map) }; 28 | println!( 29 | "{}: bucket_mask 0x{:x}, ctrl 0x{:x}, growth_left: {}, items: {}", 30 | name, arr[2], arr[3], arr[4], arr[5] 31 | ); 32 | unsafe { std::mem::transmute(arr) } 33 | } 34 | -------------------------------------------------------------------------------- /18_error_handling/error_handling.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | ) 6 | 7 | func readIssue() (string, error) { 8 | var err *naughtyError 9 | log.Printf("(in readIssue) is err nil? %v", err == nil) 10 | return "", err 11 | } 12 | 13 | func main() { 14 | issue, err := readIssue() 15 | log.Printf("(in main) is err nil? %v", err == nil) 16 | 17 | if err != nil { 18 | log.Fatalf("fatal error: %+v", err) 19 | } 20 | 21 | log.Printf("issue = %v", issue) 22 | } 23 | 24 | // 25 | 26 | type naughtyError struct{} 27 | 28 | func (ne *naughtyError) Error() string { 29 | return "oh no" 30 | } 31 | -------------------------------------------------------------------------------- /19_closure/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "closure" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /19_closure/closure1.txt: -------------------------------------------------------------------------------- 1 | (gdb) info locals 2 | c4 = closure_size::main::closure-3 (HashMap(size=1) = {["hello"] = "world"}, "tyr") 3 | c3 = closure_size::main::closure-2 (0x7fffffffd9f8) 4 | table = HashMap(size=1) = {["hello"] = "world"} 5 | name1 = "tyr" 6 | name = "tyr" 7 | c2 = closure_size::main::closure-1 8 | c1 = closure_size::main::closure-0 9 | (gdb) x/9gx &c4 10 | 0x7fffffffda60: 0x105fdc00347211ef 0x7acdd5d4ffd8bb08 11 | 0x7fffffffda70: 0x0000000000000003 0x00005555555a9ac0 12 | 0x7fffffffda80: 0x0000000000000002 0x0000000000000001 13 | 0x7fffffffda90: 0x00005555555a9bc0 0x0000000000000003 14 | 0x7fffffffdaa0: 0x0000000000000003 15 | -------------------------------------------------------------------------------- /19_closure/closure3.txt: -------------------------------------------------------------------------------- 1 | (gdb) info locals 2 | c = closure_size1::main::closure-0 (1, &[&str] {data_ptr: 0x55555559fbc0, length: 3}, "Tyr") 3 | i = 1 4 | v = &[&str] {data_ptr: 0x55555559fbc0, length: 3} 5 | data = Vec(size=3) = {"Rust", "Elixir", "Javascript"} 6 | name = "Tyr" 7 | (gdb) x/6gx &c 8 | 0x7fffffffdae0: 0x000055555559fbc0 0x0000000000000003 9 | 0x7fffffffdaf0: 0x000055555559fba0 0x0000000000000003 10 | 0x7fffffffdb00: 0x0000000000000003 0x00007ffff7d85c01 11 | -------------------------------------------------------------------------------- /19_closure/examples/callable.rs: -------------------------------------------------------------------------------- 1 | pub trait Executor { 2 | fn execute(&self, cmd: &str) -> Result; 3 | } 4 | 5 | struct BashExecutor { 6 | env: String, 7 | } 8 | 9 | impl Executor for BashExecutor { 10 | fn execute(&self, cmd: &str) -> Result { 11 | Ok(format!( 12 | "fake bash execute: env: {}, cmd: {}", 13 | self.env, cmd 14 | )) 15 | } 16 | } 17 | 18 | // 看看我给的 tonic 的例子,想想怎么实现让 27 行可以正常执行 19 | 20 | fn main() { 21 | let env = "PATH=/usr/bin".to_string(); 22 | 23 | let cmd = "cat /etc/passwd"; 24 | let r1 = execute(cmd, BashExecutor { env }); 25 | println!("{:?}", r1); 26 | 27 | // let r2 = execute(cmd, |cmd: &str| { 28 | // Ok(format!("fake fish execute: env: {}, cmd: {}", env, cmd)) 29 | // }); 30 | // println!("{:?}", r2); 31 | } 32 | 33 | fn execute(cmd: &str, exec: impl Executor) -> Result { 34 | exec.execute(cmd) 35 | } 36 | -------------------------------------------------------------------------------- /19_closure/examples/callable_result.rs: -------------------------------------------------------------------------------- 1 | pub trait Executor { 2 | fn execute(&self, cmd: &str) -> Result; 3 | } 4 | 5 | struct BashExecutor { 6 | env: String, 7 | } 8 | 9 | impl Executor for BashExecutor { 10 | fn execute(&self, cmd: &str) -> Result { 11 | Ok(format!( 12 | "fake bash execute: env: {}, cmd: {}", 13 | self.env, cmd 14 | )) 15 | } 16 | } 17 | 18 | // 看看我给的 tonic 的例子,想想怎么实现 19 | impl Executor for F 20 | where 21 | F: Fn(&str) -> Result, 22 | { 23 | fn execute(&self, cmd: &str) -> Result { 24 | self(cmd) 25 | } 26 | } 27 | 28 | fn main() { 29 | let env = "PATH=/usr/bin".to_string(); 30 | let bash = BashExecutor { env: env.clone() }; 31 | 32 | let cmd = "cat /etc/passwd"; 33 | let r1 = execute(cmd, bash); 34 | println!("{:?}", r1); 35 | 36 | let r2 = execute(cmd, |cmd: &str| { 37 | Ok(format!("fake fish execute: env: {}, cmd: {}", env, cmd)) 38 | }); 39 | println!("{:?}", r2); 40 | } 41 | 42 | fn execute(cmd: &str, exec: impl Executor) -> Result { 43 | exec.execute(cmd) 44 | } 45 | -------------------------------------------------------------------------------- /19_closure/examples/closure_size.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::HashMap, mem::size_of_val}; 2 | fn main() { 3 | // 长度为 0 4 | let c1 = || println!("hello world!"); 5 | // 和参数无关,长度也为 0 6 | let c2 = |i: i32| println!("hello: {}", i); 7 | let name = String::from("tyr"); 8 | let name1 = name.clone(); 9 | let mut table = HashMap::new(); 10 | table.insert("hello", "world"); 11 | // 如果捕获一个引用,长度为 8 12 | let c3 = || println!("hello: {}", name); 13 | // 捕获移动的数据 name1(长度 24) + table(长度 48),closure 长度 72 14 | let c4 = move || println!("hello: {}, {:?}", name1, table); 15 | let name2 = name.clone(); 16 | // 和局部变量无关,捕获了一个 String name2,closure 长度 24 17 | let c5 = move || { 18 | let x = 1; 19 | let name3 = String::from("lindsey"); 20 | println!("hello: {}, {:?}, {:?}", x, name2, name3); 21 | }; 22 | 23 | println!( 24 | "c1: {}, c2: {}, c3: {}, c4: {}, c5: {}, main: {}", 25 | size_of_val(&c1), 26 | size_of_val(&c2), 27 | size_of_val(&c3), 28 | size_of_val(&c4), 29 | size_of_val(&c5), 30 | size_of_val(&main), 31 | ) 32 | } 33 | -------------------------------------------------------------------------------- /19_closure/examples/closure_size1.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let name = String::from("Tyr"); 3 | let data = vec!["Rust", "Elixir", "Javascript"]; 4 | let v = &data[..]; 5 | let i = 1u8; 6 | let c = move || { 7 | println!("i: {:?}", i); 8 | println!("v: {:?}, name: {:?}", v, name.clone()); 9 | }; 10 | c(); 11 | println!("size of c: {}", std::mem::size_of_val(&c)); 12 | 13 | // 请问在这里,还能访问 name 么?为什么? 14 | } 15 | -------------------------------------------------------------------------------- /19_closure/examples/fn1.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let v = vec![0u8; 1024]; 3 | let v1 = vec![0u8; 1023]; 4 | 5 | // Fn,不移动所有权 6 | let mut c = |x: u64| v.len() as u64 * x; 7 | // Fn,移动所有权 8 | let mut c1 = move |x: u64| v1.len() as u64 * x; 9 | 10 | println!("direct call: {}", c(2)); 11 | println!("direct call: {}", c1(2)); 12 | 13 | println!("call: {}", call(3, &c)); 14 | println!("call: {}", call(3, &c1)); 15 | 16 | println!("call_mut: {}", call_mut(4, &mut c)); 17 | println!("call_mut: {}", call_mut(4, &mut c1)); 18 | 19 | println!("call_once: {}", call_once(5, c)); 20 | println!("call_once: {}", call_once(5, c1)); 21 | } 22 | 23 | fn call(arg: u64, c: &impl Fn(u64) -> u64) -> u64 { 24 | c(arg) 25 | } 26 | 27 | fn call_mut(arg: u64, c: &mut impl FnMut(u64) -> u64) -> u64 { 28 | c(arg) 29 | } 30 | 31 | fn call_once(arg: u64, c: impl FnOnce(u64) -> u64) -> u64 { 32 | c(arg) 33 | } 34 | -------------------------------------------------------------------------------- /19_closure/examples/fn_mut1.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let mut name = String::from("hello"); 3 | let mut name1 = String::from("hola"); 4 | 5 | // 捕获 &mut name 6 | let mut c = || { 7 | name.push_str(" Tyr"); 8 | println!("c: {}", name); 9 | }; 10 | 11 | // 捕获 mut name1,注意 name1 需要声明成 mut 12 | let mut c1 = move || { 13 | name1.push('!'); 14 | println!("c1: {}", name1); 15 | }; 16 | 17 | c(); 18 | c1(); 19 | 20 | call_mut(&mut c); 21 | call_mut(&mut c1); 22 | 23 | call_once(c); 24 | call_once(c1); 25 | } 26 | 27 | // 在作为参数时,FnMut 也要显式地使用 mut,或者 &mut 28 | fn call_mut(c: &mut impl FnMut()) { 29 | c(); 30 | } 31 | 32 | // 想想看,为啥 call_once 不需要 mut? 33 | fn call_once(c: impl FnOnce()) { 34 | c(); 35 | } 36 | -------------------------------------------------------------------------------- /19_closure/examples/fn_once1.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let name = String::from("Tyr"); 3 | // 这个闭包啥也不干,只是把捕获的参数返回去 4 | let c = move |greeting: String| (greeting, name); 5 | 6 | let result = c("hello".to_string()); 7 | 8 | println!("result: {:?}", result); 9 | 10 | // 无法再次调用 11 | // let result = c("hi".to_string()); 12 | } 13 | -------------------------------------------------------------------------------- /19_closure/examples/fn_once2.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let name = String::from("Tyr"); 3 | 4 | // 这个闭包会 clone 内部的数据返回,所以它不是 FnOnce 5 | let c = move |greeting: String| (greeting, name.clone()); 6 | 7 | // 所以 c1 可以被调用多次 8 | 9 | println!("c1 call once: {:?}", c("qiao".into())); 10 | println!("c1 call twice: {:?}", c("bonjour".into())); 11 | 12 | // 然而一旦它被当成 FnOnce 被调用,就无法被再次调用 13 | println!("result: {:?}", call_once("hi".into(), c)); 14 | 15 | // 无法再次调用 16 | // let result = c("hi".to_string()); 17 | 18 | // fn 也可以被当成 fn 调用,只要接口一致就可以 19 | println!("result: {:?}", call_once("hola".into(), not_closure)); 20 | } 21 | 22 | fn call_once(arg: String, c: impl FnOnce(String) -> (String, String)) -> (String, String) { 23 | c(arg) 24 | } 25 | 26 | fn not_closure(arg: String) -> (String, String) { 27 | (arg, "Rosie".into()) 28 | } 29 | -------------------------------------------------------------------------------- /19_closure/examples/fn_return.rs: -------------------------------------------------------------------------------- 1 | use std::{f64::consts::PI, ops::Mul}; 2 | 3 | fn main() { 4 | let c1 = curry(5); 5 | println!("5 multiply 2 is: {}", c1(2)); 6 | 7 | let adder2 = curry(PI); 8 | println!("pi multiply 4^2 is: {}", adder2(4. * 4.)); 9 | } 10 | 11 | fn curry(x: T) -> impl Fn(T) -> T 12 | where 13 | T: Mul + Copy, 14 | { 15 | move |y| x * y 16 | } 17 | -------------------------------------------------------------------------------- /19_closure/examples/thread.rs: -------------------------------------------------------------------------------- 1 | use std::thread; 2 | 3 | fn main() { 4 | let s = String::from("hello world"); 5 | 6 | let handle = thread::spawn(move || { 7 | println!("moved: {:?}", s); 8 | }); 9 | 10 | handle.join().unwrap(); 11 | } 12 | -------------------------------------------------------------------------------- /19_closure/misc/closure.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func intSeq() func() int { 6 | i := 0 7 | return func() int { 8 | i++ 9 | return i 10 | } 11 | } 12 | 13 | func main() { 14 | nextInt := intSeq() 15 | 16 | go nextInt() 17 | 18 | fmt.Println(nextInt()) 19 | fmt.Println(nextInt()) 20 | 21 | newInts := intSeq() 22 | fmt.Println(newInts()) // 1 23 | } 24 | -------------------------------------------------------------------------------- /19_closure/src/lib.rs: -------------------------------------------------------------------------------- 1 | // empty 2 | -------------------------------------------------------------------------------- /21_kv/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kv" 3 | version = "0.1.1" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | bytes = "1" # 高效处理网络 buffer 的库 8 | dashmap = "4" # 并发 HashMap 9 | http = "0.2" # 我们使用 HTTP status code 所以引入这个类型库 10 | prost = "0.9" # 处理 protobuf 的代码 11 | thiserror = "1" # 错误定义和处理 12 | tracing = "0.1" # 日志处理 13 | 14 | [dev-dependencies] 15 | anyhow = "1" # 错误处理 16 | async-prost = "0.3" # 支持把 protobuf 封装成 TCP frame 17 | futures = "0.3" # 提供 Stream trait 18 | tokio = { version = "1", features = ["rt", "rt-multi-thread", "io-util", "macros", "net" ] } # 异步网络库 19 | tracing-subscriber = "0.3" # 日志处理 20 | 21 | [build-dependencies] 22 | prost-build = "0.9" # 编译 protobuf 23 | -------------------------------------------------------------------------------- /21_kv/build.rs: -------------------------------------------------------------------------------- 1 | use std::process::Command; 2 | 3 | fn main() { 4 | let mut config = prost_build::Config::new(); 5 | config.bytes(&["."]); 6 | config.type_attribute(".", "#[derive(PartialOrd)]"); 7 | config 8 | .out_dir("src/pb") 9 | .compile_protos(&["abi.proto"], &["."]) 10 | .unwrap(); 11 | Command::new("cargo") 12 | .args(&["fmt", "--", "src/*.rs"]) 13 | .status() 14 | .expect("cargo fmt failed"); 15 | 16 | println!("cargo:rerun-if-changed=build.rs"); 17 | println!("cargo:rerun-if-changed=abi.proto"); 18 | } 19 | -------------------------------------------------------------------------------- /21_kv/examples/client.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use async_prost::AsyncProstStream; 3 | use futures::prelude::*; 4 | use kv::{CommandRequest, CommandResponse}; 5 | use tokio::net::TcpStream; 6 | use tracing::info; 7 | 8 | #[tokio::main] 9 | async fn main() -> Result<()> { 10 | tracing_subscriber::fmt::init(); 11 | 12 | let addr = "127.0.0.1:9527"; 13 | // 连接服务器 14 | let stream = TcpStream::connect(addr).await?; 15 | 16 | // 使用 AsyncProstStream 来处理 TCP Frame 17 | let mut client = 18 | AsyncProstStream::<_, CommandResponse, CommandRequest, _>::from(stream).for_async(); 19 | 20 | // 生成一个 HSET 命令 21 | let cmd = CommandRequest::new_hset("table1", "hello", "world".to_string().into()); 22 | 23 | // 发送 HSET 命令 24 | client.send(cmd).await?; 25 | if let Some(Ok(data)) = client.next().await { 26 | info!("Got response {:?}", data); 27 | } 28 | 29 | Ok(()) 30 | } 31 | -------------------------------------------------------------------------------- /21_kv/examples/dummy_server.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use async_prost::AsyncProstStream; 3 | use futures::prelude::*; 4 | use kv::{CommandRequest, CommandResponse}; 5 | use tokio::net::TcpListener; 6 | use tracing::info; 7 | 8 | #[tokio::main] 9 | async fn main() -> Result<()> { 10 | tracing_subscriber::fmt::init(); 11 | let addr = "127.0.0.1:9527"; 12 | let listener = TcpListener::bind(addr).await?; 13 | info!("Start listening on {}", addr); 14 | loop { 15 | let (stream, addr) = listener.accept().await?; 16 | info!("Client {:?} connected", addr); 17 | tokio::spawn(async move { 18 | let mut stream = 19 | AsyncProstStream::<_, CommandRequest, CommandResponse, _>::from(stream).for_async(); 20 | while let Some(Ok(msg)) = stream.next().await { 21 | info!("Got a new command: {:?}", msg); 22 | let resp = CommandResponse { 23 | status: 404, 24 | message: "Not found".to_string(), 25 | ..Default::default() 26 | }; 27 | stream.send(resp).await.unwrap(); 28 | } 29 | info!("Client {:?} disconnected", addr); 30 | }); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /21_kv/examples/server.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use async_prost::AsyncProstStream; 3 | use futures::prelude::*; 4 | use kv::{CommandRequest, CommandResponse, MemTable, Service}; 5 | use tokio::net::TcpListener; 6 | use tracing::info; 7 | 8 | #[tokio::main] 9 | async fn main() -> Result<()> { 10 | tracing_subscriber::fmt::init(); 11 | let service: Service = Service::new(MemTable::new()); 12 | let addr = "127.0.0.1:9527"; 13 | let listener = TcpListener::bind(addr).await?; 14 | info!("Start listening on {}", addr); 15 | loop { 16 | let (stream, addr) = listener.accept().await?; 17 | info!("Client {:?} connected", addr); 18 | let svc = service.clone(); 19 | tokio::spawn(async move { 20 | let mut stream = 21 | AsyncProstStream::<_, CommandRequest, CommandResponse, _>::from(stream).for_async(); 22 | while let Some(Ok(cmd)) = stream.next().await { 23 | info!("Got a new command: {:?}", cmd); 24 | let res = svc.execute(cmd); 25 | stream.send(res).await.unwrap(); 26 | } 27 | info!("Client {:?} disconnected", addr); 28 | }); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /21_kv/src/error.rs: -------------------------------------------------------------------------------- 1 | use crate::Value; 2 | use thiserror::Error; 3 | 4 | #[derive(Error, Debug, PartialEq)] 5 | pub enum KvError { 6 | #[error("Not found for table: {0}, key: {1}")] 7 | NotFound(String, String), 8 | 9 | #[error("Command is invalid: `{0}`")] 10 | InvalidCommand(String), 11 | #[error("Cannot convert value {:0} to {1}")] 12 | ConvertError(Value, &'static str), 13 | #[error("Cannot process command {0} with table: {1}, key: {2}. Error: {}")] 14 | StorageError(&'static str, String, String, String), 15 | 16 | #[error("Failed to encode protobuf message")] 17 | EncodeError(#[from] prost::EncodeError), 18 | #[error("Failed to decode protobuf message")] 19 | DecodeError(#[from] prost::DecodeError), 20 | 21 | #[error("Internal error: {0}")] 22 | Internal(String), 23 | } 24 | -------------------------------------------------------------------------------- /21_kv/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod error; 2 | mod pb; 3 | mod service; 4 | mod storage; 5 | 6 | pub use error::KvError; 7 | pub use pb::abi::*; 8 | pub use service::*; 9 | pub use storage::*; 10 | -------------------------------------------------------------------------------- /23_advanced_generics/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "advanced_generics" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /23_advanced_generics/misc/phantom.ts: -------------------------------------------------------------------------------- 1 | // NotUsed is allowed 2 | class MyNumber { 3 | inner: T; 4 | add: (x: T, y: T) => T; 5 | } 6 | -------------------------------------------------------------------------------- /23_advanced_generics/src/complex_args.rs: -------------------------------------------------------------------------------- 1 | pub fn comsume_iterator(mut f: F) 2 | where 3 | F: FnMut(i32) -> Iter, // F 是一个闭包,接受 i32,返回 Iter 类型 4 | Iter: Iterator, // Iter 是一个 Iterator,Item 是 T 类型 5 | T: std::fmt::Debug, // T 实现了 Debug trait 6 | { 7 | // 根据 F 的类型,f(10) 返回 iterator,所以可以用 for 循环 8 | for item in f(10) { 9 | println!("{:?}", item); // item 实现了 Debug trait,所以可以用 {:?} 打印 10 | } 11 | } 12 | 13 | #[cfg(test)] 14 | mod tests { 15 | use super::*; 16 | 17 | #[test] 18 | fn test_consume_iterator() { 19 | // 不会 panic 或者出错 20 | comsume_iterator(|i| 0..i) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /23_advanced_generics/src/functions.rs: -------------------------------------------------------------------------------- 1 | pub trait ImplTrait { 2 | // 允许 3 | fn impl_in_args(s: impl Into) -> String { 4 | s.into() 5 | } 6 | 7 | // 不允许 8 | // fn impl_as_return(s: String) -> impl Into { 9 | // s 10 | // } 11 | } 12 | 13 | // 可以正确编译 14 | pub fn generics_as_return_working(i: u32) -> impl Iterator { 15 | std::iter::once(i) 16 | } 17 | 18 | // 期待泛型参数,却返回一个具体类型 19 | // pub fn generics_as_return_not_working>(i: u32) -> T { 20 | // std::iter::once(i) 21 | // } 22 | 23 | // 返回 trait object 24 | pub fn trait_object_as_return_working(i: u32) -> Box> { 25 | Box::new(std::iter::once(i)) 26 | } 27 | 28 | #[cfg(test)] 29 | mod tests { 30 | use super::*; 31 | 32 | #[test] 33 | fn test_return_impl() { 34 | let mut iter = generics_as_return_working(10); 35 | assert_eq!(Some(10), iter.next()); 36 | } 37 | 38 | #[test] 39 | fn test_return_trait_object() { 40 | let mut iter = trait_object_as_return_working(10); 41 | assert_eq!(Some(10), iter.next()); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /23_advanced_generics/src/identifier.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | 3 | #[derive(Debug, Default, PartialEq, Eq)] 4 | pub struct Identifier { 5 | inner: u64, 6 | _tag: PhantomData, 7 | } 8 | 9 | #[derive(Debug, Default, PartialEq, Eq)] 10 | pub struct User { 11 | id: Identifier, 12 | } 13 | 14 | #[derive(Debug, Default, PartialEq, Eq)] 15 | pub struct Product { 16 | id: Identifier, 17 | } 18 | 19 | #[cfg(test)] 20 | mod tests { 21 | use super::*; 22 | 23 | #[test] 24 | fn id_should_not_be_the_same() { 25 | let user = User::default(); 26 | let product = Product::default(); 27 | 28 | // 两个 id 不能比较,因为他们属于不同的类型 29 | // assert_ne!(user.id, product.id); 30 | 31 | assert_eq!(user.id.inner, product.id.inner); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /23_advanced_generics/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod auth; 2 | mod complex_args; 3 | mod functions; 4 | mod identifier; 5 | mod iterator; 6 | 7 | pub use auth::*; 8 | pub use complex_args::*; 9 | pub use functions::*; 10 | pub use identifier::*; 11 | pub use iterator::*; 12 | -------------------------------------------------------------------------------- /24_advanced_trait_objects/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "advanced_trait_objects" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | 10 | [dev-dependencies] 11 | criterion = "0.3" 12 | 13 | [[bench]] 14 | name = "trait_object" 15 | harness = false 16 | -------------------------------------------------------------------------------- /24_advanced_trait_objects/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod service; 2 | mod trait_object_in_fn; 3 | 4 | pub use service::*; 5 | pub use trait_object_in_fn::*; 6 | -------------------------------------------------------------------------------- /26_kv/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kv1" 3 | version = "0.1.0" 4 | edition = "2018" 5 | 6 | [dependencies] 7 | bytes = "1" # 高效处理网络 buffer 的库 8 | dashmap = "4" # 并发 HashMap 9 | http = "0.2" # 我们使用 HTTP status code 所以引入这个类型库 10 | prost = "0.8" # 处理 protobuf 的代码 11 | sled = "0.34" # sled db 12 | thiserror = "1" # 错误定义和处理 13 | tracing = "0.1" # 日志处理 14 | 15 | [dev-dependencies] 16 | anyhow = "1" # 错误处理 17 | async-prost = "0.2.1" # 支持把 protobuf 封装成 TCP frame 18 | futures = "0.3" # 提供 Stream trait 19 | tempfile = "3" # 处理临时目录和临时文件 20 | tokio = { version = "1", features = ["rt", "rt-multi-thread", "io-util", "macros", "net" ] } # 异步网络库 21 | tracing-subscriber = "0.2" # 日志处理 22 | 23 | [build-dependencies] 24 | prost-build = "0.8" # 编译 protobuf 25 | -------------------------------------------------------------------------------- /26_kv/build.rs: -------------------------------------------------------------------------------- 1 | use std::process::Command; 2 | 3 | fn main() { 4 | let mut config = prost_build::Config::new(); 5 | config.bytes(&["."]); 6 | config.type_attribute(".", "#[derive(PartialOrd)]"); 7 | config 8 | .out_dir("src/pb") 9 | .compile_protos(&["abi.proto"], &["."]) 10 | .unwrap(); 11 | Command::new("cargo") 12 | .args(&["fmt", "--", "src/*.rs"]) 13 | .status() 14 | .expect("cargo fmt failed"); 15 | 16 | println!("cargo:rerun-if-changed=build.rs"); 17 | println!("cargo:rerun-if-changed=abi.proto"); 18 | } 19 | -------------------------------------------------------------------------------- /26_kv/examples/client.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use async_prost::AsyncProstStream; 3 | use futures::prelude::*; 4 | use kv1::{CommandRequest, CommandResponse}; 5 | use tokio::net::TcpStream; 6 | use tracing::info; 7 | 8 | #[tokio::main] 9 | async fn main() -> Result<()> { 10 | tracing_subscriber::fmt::init(); 11 | 12 | let addr = "127.0.0.1:9527"; 13 | // 连接服务器 14 | let stream = TcpStream::connect(addr).await?; 15 | 16 | // 使用 AsyncProstStream 来处理 TCP Frame 17 | let mut client = 18 | AsyncProstStream::<_, CommandResponse, CommandRequest, _>::from(stream).for_async(); 19 | 20 | // 生成一个 HSET 命令 21 | let cmd = CommandRequest::new_hset("table1", "hello", "world".to_string().into()); 22 | 23 | // 发送 HSET 命令 24 | client.send(cmd).await?; 25 | if let Some(Ok(data)) = client.next().await { 26 | info!("Got response {:?}", data); 27 | } 28 | 29 | Ok(()) 30 | } 31 | -------------------------------------------------------------------------------- /26_kv/examples/server.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use async_prost::AsyncProstStream; 3 | use futures::prelude::*; 4 | use kv1::{CommandRequest, CommandResponse, MemTable, Service, ServiceInner}; 5 | use tokio::net::TcpListener; 6 | use tracing::info; 7 | 8 | #[tokio::main] 9 | async fn main() -> Result<()> { 10 | tracing_subscriber::fmt::init(); 11 | let service: Service = ServiceInner::new(MemTable::new()).into(); 12 | let addr = "127.0.0.1:9527"; 13 | let listener = TcpListener::bind(addr).await?; 14 | info!("Start listening on {}", addr); 15 | loop { 16 | let (stream, addr) = listener.accept().await?; 17 | info!("Client {:?} connected", addr); 18 | let svc = service.clone(); 19 | tokio::spawn(async move { 20 | let mut stream = 21 | AsyncProstStream::<_, CommandRequest, CommandResponse, _>::from(stream).for_async(); 22 | while let Some(Ok(cmd)) = stream.next().await { 23 | info!("Got a new command: {:?}", cmd); 24 | let res = svc.execute(cmd); 25 | stream.send(res).await.unwrap(); 26 | } 27 | info!("Client {:?} disconnected", addr); 28 | }); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /26_kv/src/error.rs: -------------------------------------------------------------------------------- 1 | use crate::Value; 2 | use thiserror::Error; 3 | 4 | #[derive(Error, Debug, PartialEq)] 5 | pub enum KvError { 6 | #[error("Not found for table: {0}, key: {1}")] 7 | NotFound(String, String), 8 | 9 | #[error("Command is invalid: `{0}`")] 10 | InvalidCommand(String), 11 | #[error("Cannot convert value {:0} to {1}")] 12 | ConvertError(Value, &'static str), 13 | #[error("Cannot process command {0} with table: {1}, key: {2}. Error: {}")] 14 | StorageError(&'static str, String, String, String), 15 | 16 | #[error("Failed to encode protobuf message")] 17 | EncodeError(#[from] prost::EncodeError), 18 | #[error("Failed to decode protobuf message")] 19 | DecodeError(#[from] prost::DecodeError), 20 | #[error("Failed to access sled db")] 21 | SledError(#[from] sled::Error), 22 | 23 | #[error("Internal error: {0}")] 24 | Internal(String), 25 | } 26 | -------------------------------------------------------------------------------- /26_kv/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod error; 2 | mod pb; 3 | mod service; 4 | mod storage; 5 | 6 | pub use error::KvError; 7 | pub use pb::abi::*; 8 | pub use service::*; 9 | pub use storage::*; 10 | -------------------------------------------------------------------------------- /29_network/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "network" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | 10 | 11 | [dev-dependencies] 12 | anyhow = "1" 13 | bytes = "1" 14 | futures = "0.3" 15 | libp2p = { version = "0.39", features = ["tcp-tokio"] } 16 | rocket = { version = "0.5.0-rc.1", features = ["json"] } 17 | tokio = { version = "1", features = ["full"] } 18 | tokio-util = { version = "0.6", features = ["codec"] } 19 | -------------------------------------------------------------------------------- /29_network/examples/async_client.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | io::{Read, Write}, 3 | net::TcpStream, 4 | }; 5 | 6 | fn main() { 7 | let mut stream = TcpStream::connect("127.0.0.1:9527").unwrap(); 8 | // 一共写了 12 个字节 9 | stream.write_all(b"hello world!").unwrap(); 10 | 11 | let mut buf = [0u8; 17]; 12 | stream.read_exact(&mut buf).unwrap(); 13 | println!("data: {:?}", String::from_utf8_lossy(&buf)); 14 | } 15 | -------------------------------------------------------------------------------- /29_network/examples/async_listener.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | io::{Read, Write}, 3 | net::TcpListener, 4 | thread, 5 | }; 6 | 7 | fn main() { 8 | let listener = TcpListener::bind("0.0.0.0:9527").unwrap(); 9 | loop { 10 | let (mut stream, addr) = listener.accept().unwrap(); 11 | println!("Accepted a new connection: {}", addr); 12 | thread::spawn(move || { 13 | let mut buf = [0u8; 12]; 14 | stream.read_exact(&mut buf).unwrap(); 15 | println!("data: {:?}", String::from_utf8_lossy(&buf)); 16 | // 一共写了 17 个字节 17 | stream.write_all(b"glad to meet you!").unwrap(); 18 | }); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /29_network/examples/client.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | io::{Read, Write}, 3 | net::TcpStream, 4 | }; 5 | 6 | fn main() { 7 | let mut stream = TcpStream::connect("127.0.0.1:9527").unwrap(); 8 | // 一共写了 12 个字节 9 | stream.write_all(b"hello world!").unwrap(); 10 | 11 | let mut buf = [0u8; 17]; 12 | stream.read_exact(&mut buf).unwrap(); 13 | println!("data: {:?}", String::from_utf8_lossy(&buf)); 14 | } 15 | -------------------------------------------------------------------------------- /29_network/examples/framed_client.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use bytes::Bytes; 3 | use futures::{SinkExt, StreamExt}; 4 | use tokio::net::TcpStream; 5 | use tokio_util::codec::{Framed, LengthDelimitedCodec}; 6 | 7 | #[tokio::main] 8 | async fn main() -> Result<()> { 9 | let stream = TcpStream::connect("127.0.0.1:9527").await?; 10 | let mut stream = Framed::new(stream, LengthDelimitedCodec::new()); 11 | stream.send(Bytes::from("hello world")).await?; 12 | 13 | // 接收从服务器返回的数据 14 | if let Some(Ok(data)) = stream.next().await { 15 | println!("Got: {:?}", String::from_utf8_lossy(&data)); 16 | } 17 | 18 | Ok(()) 19 | } 20 | -------------------------------------------------------------------------------- /29_network/examples/framed_server.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use bytes::Bytes; 3 | use futures::{SinkExt, StreamExt}; 4 | use tokio::net::TcpListener; 5 | use tokio_util::codec::{Framed, LengthDelimitedCodec}; 6 | 7 | #[tokio::main] 8 | async fn main() -> Result<()> { 9 | let listener = TcpListener::bind("127.0.0.1:9527").await?; 10 | loop { 11 | let (stream, addr) = listener.accept().await?; 12 | println!("accepted: {:?}", addr); 13 | // LengthDelimitedCodec 默认 4 字节长度 14 | let mut stream = Framed::new(stream, LengthDelimitedCodec::new()); 15 | 16 | tokio::spawn(async move { 17 | // 接收到的消息会只包含消息主体(不包含长度) 18 | while let Some(Ok(data)) = stream.next().await { 19 | println!("Got: {:?}", String::from_utf8_lossy(&data)); 20 | // 发送的消息也脏需要发送消息主体,不需要提供长度 21 | // Framed/LengthDelimitedCodec 会自动计算并添加 22 | stream.send(Bytes::from("goodbye world!")).await.unwrap(); 23 | } 24 | }); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /29_network/examples/listener.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | io::{Read, Write}, 3 | net::TcpListener, 4 | thread, 5 | }; 6 | 7 | fn main() { 8 | let listener = TcpListener::bind("0.0.0.0:9527").unwrap(); 9 | loop { 10 | let (mut stream, addr) = listener.accept().unwrap(); 11 | println!("Accepted a new connection: {}", addr); 12 | thread::spawn(move || { 13 | let mut buf = [0u8; 12]; 14 | stream.read_exact(&mut buf).unwrap(); 15 | println!("data: {:?}", String::from_utf8_lossy(&buf)); 16 | // 一共写了 17 个字节 17 | stream.write_all(b"glad to meet you!").unwrap(); 18 | }); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /29_network/examples/rocket_server.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate rocket; 3 | 4 | use rocket::serde::json::Json; 5 | use rocket::serde::{Deserialize, Serialize}; 6 | 7 | #[derive(Serialize, Deserialize)] 8 | #[serde(crate = "rocket::serde")] 9 | struct Hello { 10 | name: String, 11 | } 12 | 13 | #[get("/", format = "json")] 14 | fn hello() -> Json { 15 | Json(Hello { name: "Tyr".into() }) 16 | } 17 | 18 | #[launch] 19 | fn rocket() -> _ { 20 | rocket::build().mount("/", routes![hello]) 21 | } 22 | -------------------------------------------------------------------------------- /29_network/src/lib.rs: -------------------------------------------------------------------------------- 1 | // see examples/* 2 | -------------------------------------------------------------------------------- /30_unsafe/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "unsafe-playground" 3 | version = "0.1.0" 4 | edition = "2021" 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" 10 | libc = "0.2" 11 | -------------------------------------------------------------------------------- /30_unsafe/examples/c_ffi.rs: -------------------------------------------------------------------------------- 1 | use std::mem::transmute; 2 | 3 | fn main() { 4 | let data = unsafe { 5 | let p = libc::malloc(8); 6 | let arr: &mut [u8; 8] = &mut *(p as *mut [u8; 8]); 7 | arr 8 | }; 9 | 10 | data.copy_from_slice(&[1, 2, 3, 4, 5, 6, 7, 8]); 11 | 12 | println!("data: {:?}", data); 13 | 14 | unsafe { libc::free(transmute(data)) }; 15 | } 16 | -------------------------------------------------------------------------------- /30_unsafe/examples/raw_pointer.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let mut age = 18; 3 | 4 | // 不可变引用 5 | let r1 = &age as *const i32; 6 | // 可变引用 7 | let r2 = &mut age as *mut i32; 8 | 9 | // 使用裸指针,可以绕过 immutable / mutable borrow rule 10 | 11 | // 然而,对指针解引用需要使用 unsafe 12 | unsafe { 13 | println!("r1: {}, r2: {}", *r1, *r2); 14 | } 15 | } 16 | 17 | // fn immutable_mutable_cant_coexist() { 18 | // let mut age = 18; 19 | // let r1 = &age; 20 | // // 编译错误 21 | // let r2 = &mut age; 22 | 23 | // println!("r1: {}, r2: {}", *r1, *r2); 24 | // } 25 | -------------------------------------------------------------------------------- /30_unsafe/examples/raw_pointer_crash.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // 裸指针指向一个有问题的地址 3 | let r1 = 0xdeadbeef as *mut u32; 4 | 5 | println!("so far so good!"); 6 | 7 | unsafe { 8 | // 程序崩溃 9 | *r1 += 1; 10 | println!("r1: {}", *r1); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /30_unsafe/examples/rc_send.rs: -------------------------------------------------------------------------------- 1 | use std::{cell::RefCell, rc::Rc, thread}; 2 | 3 | #[derive(Debug, Default, Clone)] 4 | struct Evil { 5 | data: Rc>, 6 | } 7 | 8 | // 为 Evil 强行实现 Send,这会让 Rc 整个紊乱 9 | unsafe impl Send for Evil {} 10 | 11 | fn main() { 12 | let v = Evil::default(); 13 | let v1 = v.clone(); 14 | let v2 = v.clone(); 15 | 16 | let t1 = thread::spawn(move || { 17 | let v3 = v; 18 | let mut data = v3.data.borrow_mut(); 19 | *data += 1; 20 | println!("v3: {:?}", data); 21 | }); 22 | 23 | let t2 = thread::spawn(move || { 24 | let v4 = v1; 25 | let mut data = v4.data.borrow_mut(); 26 | *data += 1; 27 | println!("v4: {:?}", data); 28 | }); 29 | 30 | t2.join().unwrap(); 31 | t1.join().unwrap(); 32 | 33 | let mut data = v2.data.borrow_mut(); 34 | *data += 1; 35 | 36 | println!("v2: {:?}", data); 37 | } 38 | -------------------------------------------------------------------------------- /30_unsafe/examples/static_mutable.rs: -------------------------------------------------------------------------------- 1 | use std::thread; 2 | 3 | static mut COUNTER: usize = 1; 4 | 5 | fn main() { 6 | let t1 = thread::spawn(move || { 7 | unsafe { COUNTER += 10 }; 8 | }); 9 | 10 | let t2 = thread::spawn(move || { 11 | unsafe { COUNTER *= 10 }; 12 | }); 13 | 14 | t2.join().unwrap(); 15 | t1.join().unwrap(); 16 | 17 | unsafe { println!("COUNTER: {}", COUNTER) }; 18 | } 19 | -------------------------------------------------------------------------------- /30_unsafe/examples/static_mutable_safe.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | sync::atomic::{AtomicUsize, Ordering}, 3 | thread, 4 | }; 5 | 6 | static COUNTER: AtomicUsize = AtomicUsize::new(1); 7 | 8 | fn main() { 9 | let t1 = thread::spawn(move || { 10 | COUNTER.fetch_add(10, Ordering::SeqCst); 11 | }); 12 | 13 | let t2 = thread::spawn(move || { 14 | COUNTER 15 | .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |v| Some(v * 10)) 16 | .unwrap(); 17 | }); 18 | 19 | t2.join().unwrap(); 20 | t1.join().unwrap(); 21 | 22 | println!("COUNTER: {}", COUNTER.load(Ordering::Relaxed)); 23 | } 24 | -------------------------------------------------------------------------------- /30_unsafe/examples/static_mutable_safe1.rs: -------------------------------------------------------------------------------- 1 | use lazy_static::lazy_static; 2 | use std::{collections::HashMap, sync::Mutex, thread}; 3 | 4 | // 使用 lazy_static 初始化复杂的结构 5 | lazy_static! { 6 | // 使用 Mutex / RwLock 来提供安全的并发写访问 7 | static ref STORE: Mutex> = Mutex::new(HashMap::new()); 8 | } 9 | 10 | fn main() { 11 | let t1 = thread::spawn(move || { 12 | let mut store = STORE.lock().unwrap(); 13 | store.insert("hello", b"world"); 14 | }); 15 | 16 | let t2 = thread::spawn(move || { 17 | let mut store = STORE.lock().unwrap(); 18 | store.insert("goodbye", b"world"); 19 | }); 20 | 21 | t2.join().unwrap(); 22 | t1.join().unwrap(); 23 | 24 | println!("store: {:?}", STORE.lock().unwrap()); 25 | } 26 | -------------------------------------------------------------------------------- /30_unsafe/examples/unsafe_fn1.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | fn main() { 4 | let map = HashMap::new(); 5 | let mut map = explain("empty", map); 6 | 7 | map.insert(String::from("a"), 1); 8 | explain("added 1", map); 9 | } 10 | 11 | // HashMap 结构有两个 u64 的 RandomState,然后是四个 usize, 12 | // 分别是 bucket_mask, ctrl, growth_left 和 items 13 | // 我们 transmute 打印之后,再 transmute 回去 14 | fn explain(name: &str, map: HashMap) -> HashMap { 15 | let arr: [usize; 6] = unsafe { std::mem::transmute(map) }; 16 | println!( 17 | "{}: bucket_mask 0x{:x}, ctrl 0x{:x}, growth_left: {}, items: {}", 18 | name, arr[2], arr[3], arr[4], arr[5] 19 | ); 20 | 21 | // 因为 std:mem::transmute 是一个 unsafe 函数,所以我们需要 unsafe 22 | unsafe { std::mem::transmute(arr) } 23 | } 24 | -------------------------------------------------------------------------------- /30_unsafe/examples/unsafe_fn2.rs: -------------------------------------------------------------------------------- 1 | use std::str; 2 | 3 | fn main() { 4 | let data = b"hello world!"; 5 | 6 | // Safety: 从上下文中我们可以保证 data 只包含合法的 utf8 字符 7 | let s = unsafe { str::from_utf8_unchecked(data) }; 8 | 9 | println!("s: {}", s); 10 | } 11 | -------------------------------------------------------------------------------- /30_unsafe/examples/unsafe_trait.rs: -------------------------------------------------------------------------------- 1 | /// # Safety 2 | /// 实现这个 trait 的开发者要保证实现是内存安全的 3 | unsafe trait Foo { 4 | fn foo(&self); 5 | } 6 | 7 | trait Bar { 8 | // 调用这个函数的人要保证调用是安全的 9 | unsafe fn bar(&self); 10 | } 11 | 12 | struct Nonsense; 13 | 14 | /// # Safety 15 | /// example 16 | unsafe impl Foo for Nonsense { 17 | fn foo(&self) { 18 | println!("foo!"); 19 | } 20 | } 21 | 22 | impl Bar for Nonsense { 23 | unsafe fn bar(&self) { 24 | println!("bar!"); 25 | } 26 | } 27 | 28 | fn main() { 29 | let nonsense = Nonsense; 30 | // 调用者无需关心 safety 31 | nonsense.foo(); 32 | 33 | // 调用者需要为 safety 负责 34 | unsafe { nonsense.bar() }; 35 | } 36 | -------------------------------------------------------------------------------- /30_unsafe/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | #[test] 4 | fn it_works() { 5 | let result = 2 + 2; 6 | assert_eq!(result, 4); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /31_ffi/bzlib_sys/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bzlib-sys" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | anyhow = "1" 8 | 9 | [build-dependencies] 10 | bindgen = "0.59" 11 | -------------------------------------------------------------------------------- /31_ffi/bzlib_sys/README.md: -------------------------------------------------------------------------------- 1 | # Windows 支持 2 | 3 | 因为 windows 系统中没有 bz2,所以你只能通过源码编译。具体方法请参考 [bzip2-sys](https://github.com/alexcrichton/bzip2-rs/tree/master/bzip2-sys)。 4 | -------------------------------------------------------------------------------- /31_ffi/bzlib_sys/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // 告诉 rustc 需要 link bzip2 3 | println!("cargo:rustc-link-lib=bz2"); 4 | 5 | // 告诉 cargo 当 wrapper.h 变化时重新运行 6 | println!("cargo:rerun-if-changed=wrapper.h"); 7 | 8 | // 配置 bindgen,并生成 Bindings 结构 9 | let bindings = bindgen::Builder::default() 10 | .header("wrapper.h") 11 | .parse_callbacks(Box::new(bindgen::CargoCallbacks)) 12 | .generate() 13 | .expect("Unable to generate bindings"); 14 | 15 | // 生成 Rust 代码 16 | bindings 17 | .write_to_file("src/bindings.rs") 18 | .expect("Failed to write bindings"); 19 | } 20 | -------------------------------------------------------------------------------- /31_ffi/bzlib_sys/wrapper.h: -------------------------------------------------------------------------------- 1 | #include 2 | -------------------------------------------------------------------------------- /31_ffi/ffi/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ffi" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [lib] 7 | crate-type = ["cdylib"] 8 | 9 | 10 | [dependencies] 11 | libc = "0.2" 12 | 13 | [build-dependencies] 14 | cbindgen = "0.20" 15 | -------------------------------------------------------------------------------- /31_ffi/ffi/bindings.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | extern "C" { 8 | 9 | const char *hello_world(); 10 | 11 | /// # Safety 12 | /// 这个函数是不安全的,别调! 13 | const char *hello_bad(const char *name); 14 | 15 | const char *hello(const char *name); 16 | 17 | /// # Safety 18 | /// 提供给 C 侧释放字符串指针,调用者需要保证指针来自 Rust 19 | void free_str(char *s); 20 | 21 | } // extern "C" 22 | -------------------------------------------------------------------------------- /31_ffi/ffi/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | fn main() { 3 | let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); 4 | 5 | cbindgen::Builder::new() 6 | .with_crate(crate_dir) 7 | .generate() 8 | .expect("Unable to generate bindings") 9 | .write_to_file("bindings.h"); 10 | } 11 | -------------------------------------------------------------------------------- /31_ffi/ffi_math/..swiftdoc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/405-studio/geektime-rust/3c57ef418118eacc24d2e35acb470aa4e6a6a672/31_ffi/ffi_math/..swiftdoc -------------------------------------------------------------------------------- /31_ffi/ffi_math/..swiftsourceinfo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/405-studio/geektime-rust/3c57ef418118eacc24d2e35acb470aa4e6a6a672/31_ffi/ffi_math/..swiftsourceinfo -------------------------------------------------------------------------------- /31_ffi/ffi_math/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ffi-math" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [lib] 7 | crate-type = ["cdylib"] 8 | 9 | [dependencies] 10 | uniffi = "0.15" 11 | uniffi_macros = "0.15" 12 | 13 | [build-dependencies] 14 | uniffi_build = {version = "0.15", features = [ "builtin-bindgen" ]} 15 | -------------------------------------------------------------------------------- /31_ffi/ffi_math/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | uniffi_build::generate_scaffolding("./src/math.udl").unwrap(); 3 | } 4 | -------------------------------------------------------------------------------- /31_ffi/ffi_math/ffi_math.swiftmodule: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/405-studio/geektime-rust/3c57ef418118eacc24d2e35acb470aa4e6a6a672/31_ffi/ffi_math/ffi_math.swiftmodule -------------------------------------------------------------------------------- /31_ffi/ffi_math/libffi_math.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/405-studio/geektime-rust/3c57ef418118eacc24d2e35acb470aa4e6a6a672/31_ffi/ffi_math/libffi_math.dylib -------------------------------------------------------------------------------- /31_ffi/ffi_math/src/lib.rs: -------------------------------------------------------------------------------- 1 | uniffi_macros::include_scaffolding!("math"); 2 | 3 | pub fn add(a: u32, b: u32) -> u32 { 4 | a + b 5 | } 6 | 7 | pub fn hello(name: &str) -> String { 8 | format!("hello {}!", name) 9 | } 10 | -------------------------------------------------------------------------------- /31_ffi/ffi_math/src/math.udl: -------------------------------------------------------------------------------- 1 | namespace math { 2 | u32 add(u32 a, u32 b); 3 | string hello([ByRef]string name); 4 | }; 5 | -------------------------------------------------------------------------------- /31_ffi/ffi_math/src/mathFFI.modulemap: -------------------------------------------------------------------------------- 1 | // This file was autogenerated by some hot garbage in the `uniffi` crate. 2 | // Trust me, you don't want to mess with it! 3 | module mathFFI { 4 | header "mathFFI.h" 5 | export * 6 | } 7 | -------------------------------------------------------------------------------- /32_xunmi_py/.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | target 3 | .ipynb_* 4 | __pycache__ 5 | .pytest_cache 6 | *.so 7 | -------------------------------------------------------------------------------- /32_xunmi_py/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "xunmi-py" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [lib] 7 | name = "xunmi" 8 | crate-type = ["cdylib"] 9 | doc = false 10 | 11 | [dependencies] 12 | pyo3 = {version = "0.14", features = ["extension-module"]} 13 | serde_json = "1" 14 | xunmi = "0.2" 15 | 16 | [build-dependencies] 17 | pyo3-build-config = "0.14" 18 | -------------------------------------------------------------------------------- /32_xunmi_py/Makefile: -------------------------------------------------------------------------------- 1 | # 如果你的 BUILD_DIR 不同,可以 make BUILD_DIR= 2 | BUILD_DIR := target/release 3 | 4 | SRCS := $(wildcard src/*.rs) Cargo.toml 5 | NAME = xunmi 6 | TARGET = lib$(NAME) 7 | BUILD_FILE = $(BUILD_DIR)/$(TARGET).dylib 8 | BUILD_FILE1 = $(BUILD_DIR)/$(TARGET).so 9 | TARGET_FILE = $(NAME)/$(NAME).so 10 | 11 | all: $(TARGET_FILE) 12 | 13 | test: $(TARGET_FILE) 14 | python3 -m pytest 15 | 16 | $(TARGET_FILE): $(BUILD_FILE1) 17 | @cp $(BUILD_FILE1) $(TARGET_FILE) 18 | 19 | $(BUILD_FILE1): $(SRCS) 20 | @cargo build --release 21 | @mv $(BUILD_FILE) $(BUILD_FILE1) || true 22 | 23 | PHONY: test all 24 | -------------------------------------------------------------------------------- /32_xunmi_py/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/405-studio/geektime-rust/3c57ef418118eacc24d2e35acb470aa4e6a6a672/32_xunmi_py/README.md -------------------------------------------------------------------------------- /32_xunmi_py/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // 只有 build.rs 更改后才重新编译 3 | println!("cargo:rerun-if-changed=build.rs"); 4 | // 添加 5 | pyo3_build_config::add_extension_module_link_args(); 6 | } 7 | -------------------------------------------------------------------------------- /32_xunmi_py/fixtures/config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | path: /tmp/searcher_index 3 | schema: 4 | - name: id 5 | type: u64 6 | options: 7 | indexed: true 8 | fast: single 9 | stored: true 10 | - name: url 11 | type: text 12 | options: 13 | indexing: ~ 14 | stored: true 15 | - name: title 16 | type: text 17 | options: 18 | indexing: 19 | record: position 20 | tokenizer: CANG_JIE 21 | stored: true 22 | - name: content 23 | type: text 24 | options: 25 | indexing: 26 | record: position 27 | tokenizer: CANG_JIE 28 | stored: false 29 | text_lang: 30 | chinese: true 31 | writer_memory: 100000000 32 | -------------------------------------------------------------------------------- /32_xunmi_py/xunmi-py/.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | target 3 | .ipynb_* 4 | __pycache__ 5 | .pytest_cache 6 | *.so 7 | -------------------------------------------------------------------------------- /32_xunmi_py/xunmi-py/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "xunmi-py" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [lib] 7 | name = "xunmi" 8 | crate-type = ["cdylib"] 9 | 10 | [dependencies] 11 | pyo3 = {version = "0.14", features = ["extension-module"]} 12 | serde_json = "1" 13 | xunmi = "0.2" 14 | 15 | [build-dependencies] 16 | pyo3-build-config = "0.14" 17 | -------------------------------------------------------------------------------- /32_xunmi_py/xunmi-py/Makefile: -------------------------------------------------------------------------------- 1 | # 如果你的 BUILD_DIR 不同,可以 make BUILD_DIR= 2 | BUILD_DIR := target/release 3 | 4 | SRCS := $(wildcard src/*.rs) Cargo.toml 5 | NAME = xunmi 6 | TARGET = lib$(NAME) 7 | BUILD_FILE = $(BUILD_DIR)/$(TARGET).dylib 8 | BUILD_FILE1 = $(BUILD_DIR)/$(TARGET).so 9 | TARGET_FILE = $(NAME)/$(NAME).so 10 | 11 | all: $(TARGET_FILE) 12 | 13 | test: $(TARGET_FILE) 14 | python3 -m pytest 15 | 16 | $(TARGET_FILE): $(BUILD_FILE1) 17 | @cp $(BUILD_FILE1) $(TARGET_FILE) 18 | 19 | $(BUILD_FILE1): $(SRCS) 20 | @cargo build --release 21 | @mv $(BUILD_FILE) $(BUILD_FILE1) || true 22 | 23 | PHONY: test all 24 | -------------------------------------------------------------------------------- /32_xunmi_py/xunmi-py/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/405-studio/geektime-rust/3c57ef418118eacc24d2e35acb470aa4e6a6a672/32_xunmi_py/xunmi-py/README.md -------------------------------------------------------------------------------- /32_xunmi_py/xunmi-py/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // 只有 build.rs 更改后才重新编译 3 | println!("cargo:rerun-if-changed=build.rs"); 4 | // 添加 5 | pyo3_build_config::add_extension_module_link_args(); 6 | } 7 | -------------------------------------------------------------------------------- /32_xunmi_py/xunmi-py/fixtures/config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | path: /tmp/searcher_index 3 | schema: 4 | - name: id 5 | type: u64 6 | options: 7 | indexed: true 8 | fast: single 9 | stored: true 10 | - name: url 11 | type: text 12 | options: 13 | indexing: ~ 14 | stored: true 15 | - name: title 16 | type: text 17 | options: 18 | indexing: 19 | record: position 20 | tokenizer: CANG_JIE 21 | stored: true 22 | - name: content 23 | type: text 24 | options: 25 | indexing: 26 | record: position 27 | tokenizer: CANG_JIE 28 | stored: false 29 | text_lang: 30 | chinese: true 31 | writer_memory: 100000000 32 | -------------------------------------------------------------------------------- /32_xunmi_py/xunmi-py/xunmi/__init__.py: -------------------------------------------------------------------------------- 1 | from .xunmi import * 2 | -------------------------------------------------------------------------------- /32_xunmi_py/xunmi/__init__.py: -------------------------------------------------------------------------------- 1 | from .xunmi import * 2 | -------------------------------------------------------------------------------- /33_concurrency/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "concurrency" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | 10 | [dev-dependencies] 11 | actix = "0.12" 12 | anyhow = "1" 13 | lazy_static = "1" 14 | -------------------------------------------------------------------------------- /33_concurrency/examples/condvar.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{Arc, Condvar, Mutex}; 2 | use std::thread; 3 | use std::time::Duration; 4 | 5 | #[allow(clippy::all)] 6 | fn main() { 7 | let pair = Arc::new((Mutex::new(false), Condvar::new())); 8 | let pair2 = Arc::clone(&pair); 9 | 10 | thread::spawn(move || { 11 | let (lock, cvar) = &*pair2; 12 | 13 | // 通过离开作用域来释放锁,否则主线程会一直阻塞在获取锁上 14 | { 15 | let mut started = lock.lock().unwrap(); 16 | *started = true; 17 | } 18 | 19 | eprintln!("I'm a happy worker!"); 20 | // 通知主线程 21 | cvar.notify_one(); 22 | loop { 23 | thread::sleep(Duration::from_secs(1)); 24 | println!("working..."); 25 | } 26 | }); 27 | 28 | // 等待工作线程的通知 29 | let (lock, cvar) = &*pair; 30 | let mut started = lock.lock().unwrap(); 31 | while !*started { 32 | started = cvar.wait(started).unwrap(); 33 | } 34 | eprintln!("Worker started!"); 35 | 36 | // 等待 worker 线程 37 | thread::sleep(Duration::from_secs(3600)); 38 | } 39 | -------------------------------------------------------------------------------- /33_concurrency/src/lib.rs: -------------------------------------------------------------------------------- 1 | // see examples 2 | -------------------------------------------------------------------------------- /35_con_utils/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "con_utils" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | anyhow = "1" 10 | -------------------------------------------------------------------------------- /35_con_utils/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod channel; 2 | -------------------------------------------------------------------------------- /36_kv/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kv2" 3 | version = "0.1.0" 4 | edition = "2018" 5 | 6 | [[bin]] 7 | name = "kvs" 8 | path = "src/server.rs" 9 | doc = false 10 | 11 | [[bin]] 12 | name = "kvc" 13 | path = "src/client.rs" 14 | doc = false 15 | 16 | [dependencies] 17 | anyhow = "1" # 错误处理 18 | bytes = "1" # 高效处理网络 buffer 的库 19 | dashmap = "4" # 并发 HashMap 20 | flate2 = "1" # gzip 压缩 21 | http = "0.2" # 我们使用 HTTP status code 所以引入这个类型库 22 | prost = "0.8" # 处理 protobuf 的代码 23 | sled = "0.34" # sled db 24 | thiserror = "1" # 错误定义和处理 25 | tokio = { version = "1", features = ["full" ] } # 异步网络库 26 | tracing = "0.1" # 日志处理 27 | tracing-subscriber = "0.2" # 日志处理 28 | 29 | [dev-dependencies] 30 | async-prost = "0.2.1" # 支持把 protobuf 封装成 TCP frame 31 | futures = "0.3" # 提供 Stream trait 32 | tempfile = "3" # 处理临时目录和临时文件 33 | tokio-util = { version = "0.6", features = ["codec"]} 34 | 35 | [build-dependencies] 36 | prost-build = "0.8" # 编译 protobuf 37 | -------------------------------------------------------------------------------- /36_kv/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let mut config = prost_build::Config::new(); 3 | config.bytes(&["."]); 4 | config.type_attribute(".", "#[derive(PartialOrd)]"); 5 | config 6 | .out_dir("src/pb") 7 | .compile_protos(&["abi.proto"], &["."]) 8 | .unwrap(); 9 | 10 | println!("cargo:rerun-if-changed=build.rs"); 11 | println!("cargo:rerun-if-changed=abi.proto"); 12 | } 13 | -------------------------------------------------------------------------------- /36_kv/examples/client.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use async_prost::AsyncProstStream; 3 | use futures::prelude::*; 4 | use kv2::{CommandRequest, CommandResponse}; 5 | use tokio::net::TcpStream; 6 | use tracing::info; 7 | 8 | #[tokio::main] 9 | async fn main() -> Result<()> { 10 | tracing_subscriber::fmt::init(); 11 | 12 | let addr = "127.0.0.1:9527"; 13 | // 连接服务器 14 | let stream = TcpStream::connect(addr).await?; 15 | 16 | // 使用 AsyncProstStream 来处理 TCP Frame 17 | let mut client = 18 | AsyncProstStream::<_, CommandResponse, CommandRequest, _>::from(stream).for_async(); 19 | 20 | // 生成一个 HSET 命令 21 | let cmd = CommandRequest::new_hset("table1", "hello", "world".to_string().into()); 22 | 23 | // 发送 HSET 命令 24 | client.send(cmd).await?; 25 | if let Some(Ok(data)) = client.next().await { 26 | info!("Got response {:?}", data); 27 | } 28 | 29 | Ok(()) 30 | } 31 | -------------------------------------------------------------------------------- /36_kv/examples/server.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use async_prost::AsyncProstStream; 3 | use futures::prelude::*; 4 | use kv2::{CommandRequest, CommandResponse, MemTable, Service, ServiceInner}; 5 | use tokio::net::TcpListener; 6 | use tracing::info; 7 | 8 | #[tokio::main] 9 | async fn main() -> Result<()> { 10 | tracing_subscriber::fmt::init(); 11 | let service: Service = ServiceInner::new(MemTable::new()).into(); 12 | let addr = "127.0.0.1:9527"; 13 | let listener = TcpListener::bind(addr).await?; 14 | info!("Start listening on {}", addr); 15 | loop { 16 | let (stream, addr) = listener.accept().await?; 17 | info!("Client {:?} connected", addr); 18 | let svc = service.clone(); 19 | tokio::spawn(async move { 20 | let mut stream = 21 | AsyncProstStream::<_, CommandRequest, CommandResponse, _>::from(stream).for_async(); 22 | while let Some(Ok(cmd)) = stream.next().await { 23 | info!("Got a new command: {:?}", cmd); 24 | let res = svc.execute(cmd); 25 | stream.send(res).await.unwrap(); 26 | } 27 | info!("Client {:?} disconnected", addr); 28 | }); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /36_kv/src/client.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use kv2::{CommandRequest, ProstClientStream}; 3 | use tokio::net::TcpStream; 4 | use tracing::info; 5 | 6 | #[tokio::main] 7 | async fn main() -> Result<()> { 8 | tracing_subscriber::fmt::init(); 9 | 10 | let addr = "127.0.0.1:9527"; 11 | // 连接服务器 12 | let stream = TcpStream::connect(addr).await?; 13 | 14 | let mut client = ProstClientStream::new(stream); 15 | 16 | // 生成一个 HSET 命令 17 | let cmd = CommandRequest::new_hset("table1", "hello", "world".to_string().into()); 18 | 19 | // 发送 HSET 命令 20 | let data = client.execute(cmd).await?; 21 | info!("Got response {:?}", data); 22 | 23 | Ok(()) 24 | } 25 | -------------------------------------------------------------------------------- /36_kv/src/error.rs: -------------------------------------------------------------------------------- 1 | use crate::Value; 2 | use thiserror::Error; 3 | 4 | #[derive(Error, Debug)] 5 | pub enum KvError { 6 | #[error("Not found for table: {0}, key: {1}")] 7 | NotFound(String, String), 8 | #[error("Frame is larger than max size")] 9 | FrameError, 10 | #[error("Command is invalid: `{0}`")] 11 | InvalidCommand(String), 12 | #[error("Cannot convert value {:0} to {1}")] 13 | ConvertError(Value, &'static str), 14 | #[error("Cannot process command {0} with table: {1}, key: {2}. Error: {}")] 15 | StorageError(&'static str, String, String, String), 16 | 17 | #[error("Failed to encode protobuf message")] 18 | EncodeError(#[from] prost::EncodeError), 19 | #[error("Failed to decode protobuf message")] 20 | DecodeError(#[from] prost::DecodeError), 21 | #[error("Failed to access sled db")] 22 | SledError(#[from] sled::Error), 23 | #[error("I/O error")] 24 | IoError(#[from] std::io::Error), 25 | 26 | #[error("Internal error: {0}")] 27 | Internal(String), 28 | } 29 | -------------------------------------------------------------------------------- /36_kv/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod error; 2 | mod network; 3 | mod pb; 4 | mod service; 5 | mod storage; 6 | 7 | pub use error::KvError; 8 | pub use network::*; 9 | pub use pb::abi::*; 10 | pub use service::*; 11 | pub use storage::*; 12 | -------------------------------------------------------------------------------- /36_kv/src/server.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use kv2::{MemTable, ProstServerStream, Service, ServiceInner}; 3 | use tokio::net::TcpListener; 4 | use tracing::info; 5 | 6 | #[tokio::main] 7 | async fn main() -> Result<()> { 8 | tracing_subscriber::fmt::init(); 9 | let addr = "127.0.0.1:9527"; 10 | let service: Service = ServiceInner::new(MemTable::new()).into(); 11 | let listener = TcpListener::bind(addr).await?; 12 | info!("Start listening on {}", addr); 13 | loop { 14 | let (stream, addr) = listener.accept().await?; 15 | info!("Client {:?} connected", addr); 16 | let stream = ProstServerStream::new(stream, service.clone()); 17 | tokio::spawn(async move { stream.process().await }); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /37_kv/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/405-studio/geektime-rust/3c57ef418118eacc24d2e35acb470aa4e6a6a672/37_kv/.gitignore -------------------------------------------------------------------------------- /37_kv/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kv3" 3 | version = "0.1.0" 4 | edition = "2018" 5 | 6 | [[bin]] 7 | name = "kvs" 8 | path = "src/server.rs" 9 | doc = false 10 | 11 | [[bin]] 12 | name = "kvc" 13 | path = "src/client.rs" 14 | doc = false 15 | 16 | [dependencies] 17 | anyhow = "1" # 错误处理 18 | bytes = "1" # 高效处理网络 buffer 的库 19 | dashmap = "4" # 并发 HashMap 20 | flate2 = "1" # gzip 压缩 21 | futures = "0.3" # 提供 Stream trait 22 | http = "0.2" # 我们使用 HTTP status code 所以引入这个类型库 23 | prost = "0.8" # 处理 protobuf 的代码 24 | rustls-native-certs = "0.5" 25 | sled = "0.34" # sled db 26 | thiserror = "1" # 错误定义和处理 27 | tokio = { version = "1", features = ["full" ] } # 异步网络库 28 | tokio-rustls = "0.22" 29 | tracing = "0.1" # 日志处理 30 | tracing-subscriber = "0.2" # 日志处理 31 | 32 | [dev-dependencies] 33 | async-prost = "0.2.1" # 支持把 protobuf 封装成 TCP frame 34 | certify = "0.3" 35 | tempfile = "3" # 处理临时目录和临时文件 36 | tokio-util = { version = "0.6", features = ["codec"]} 37 | 38 | [build-dependencies] 39 | prost-build = "0.8" # 编译 protobuf 40 | -------------------------------------------------------------------------------- /37_kv/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let mut config = prost_build::Config::new(); 3 | config.bytes(&["."]); 4 | config.type_attribute(".", "#[derive(PartialOrd)]"); 5 | config 6 | .out_dir("src/pb") 7 | .compile_protos(&["abi.proto"], &["."]) 8 | .unwrap(); 9 | 10 | println!("cargo:rerun-if-changed=build.rs"); 11 | println!("cargo:rerun-if-changed=abi.proto"); 12 | } 13 | -------------------------------------------------------------------------------- /37_kv/examples/client.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use async_prost::AsyncProstStream; 3 | use futures::prelude::*; 4 | use kv3::{CommandRequest, CommandResponse}; 5 | use tokio::net::TcpStream; 6 | use tracing::info; 7 | 8 | #[tokio::main] 9 | async fn main() -> Result<()> { 10 | tracing_subscriber::fmt::init(); 11 | 12 | let addr = "127.0.0.1:9527"; 13 | // 连接服务器 14 | let stream = TcpStream::connect(addr).await?; 15 | 16 | // 使用 AsyncProstStream 来处理 TCP Frame 17 | let mut client = 18 | AsyncProstStream::<_, CommandResponse, CommandRequest, _>::from(stream).for_async(); 19 | 20 | // 生成一个 HSET 命令 21 | let cmd = CommandRequest::new_hset("table1", "hello", "world".to_string().into()); 22 | 23 | // 发送 HSET 命令 24 | client.send(cmd).await?; 25 | if let Some(Ok(data)) = client.next().await { 26 | info!("Got response {:?}", data); 27 | } 28 | 29 | Ok(()) 30 | } 31 | -------------------------------------------------------------------------------- /37_kv/examples/server.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use async_prost::AsyncProstStream; 3 | use futures::prelude::*; 4 | use kv3::{CommandRequest, CommandResponse, MemTable, Service, ServiceInner}; 5 | use tokio::net::TcpListener; 6 | use tracing::info; 7 | 8 | #[tokio::main] 9 | async fn main() -> Result<()> { 10 | tracing_subscriber::fmt::init(); 11 | let service: Service = ServiceInner::new(MemTable::new()).into(); 12 | let addr = "127.0.0.1:9527"; 13 | let listener = TcpListener::bind(addr).await?; 14 | info!("Start listening on {}", addr); 15 | loop { 16 | let (stream, addr) = listener.accept().await?; 17 | info!("Client {:?} connected", addr); 18 | let svc = service.clone(); 19 | tokio::spawn(async move { 20 | let mut stream = 21 | AsyncProstStream::<_, CommandRequest, CommandResponse, _>::from(stream).for_async(); 22 | while let Some(Ok(cmd)) = stream.next().await { 23 | info!("Got a new command: {:?}", cmd); 24 | let res = svc.execute(cmd); 25 | stream.send(res).await.unwrap(); 26 | } 27 | info!("Client {:?} disconnected", addr); 28 | }); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /37_kv/fixtures/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/405-studio/geektime-rust/3c57ef418118eacc24d2e35acb470aa4e6a6a672/37_kv/fixtures/.gitkeep -------------------------------------------------------------------------------- /37_kv/fixtures/ca.cert: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIBeDCCASqgAwIBAgIJAK/ACf4lHrMIMAUGAytlcDAzMQswCQYDVQQGDAJDTjES 3 | MBAGA1UECgwJQWNtZSBJbmMuMRAwDgYDVQQDDAdBY21lIENBMB4XDTIxMDkyNjAx 4 | MjU1OVoXDTMxMDkyNDAxMjU1OVowMzELMAkGA1UEBgwCQ04xEjAQBgNVBAoMCUFj 5 | bWUgSW5jLjEQMA4GA1UEAwwHQWNtZSBDQTAqMAUGAytlcAMhAO6QG6Ma8p4xEZ0V 6 | 9VUutcHGutlezoR4E3geBYVojMSKo1swWTATBgNVHREEDDAKgghhY21lLmluYzAd 7 | BgNVHQ4EFgQU8kevGAonItSdc8VGWa74jwYUnHgwEgYDVR0TAQH/BAgwBgEB/wIB 8 | EDAPBgNVHQ8BAf8EBQMDBwYAMAUGAytlcANBAIzbgTAiy6SHCQxfJhlQAs1dIYgU 9 | jwyjxoeYKv/jgGoJd9fMBXsD94tEtyOwF6ph6JEJh0SwCLZrQw2OIp/H7QU= 10 | -----END CERTIFICATE----- 11 | -------------------------------------------------------------------------------- /37_kv/fixtures/ca.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MFMCAQEwBQYDK2VwBCIEIDGUGlTZG5ZZ0NC/qfb+A96ofGsg4pPchKss2VodfQwf 3 | oSMDIQDukBujGvKeMRGdFfVVLrXBxrrZXs6EeBN4HgWFaIzEig== 4 | -----END PRIVATE KEY----- 5 | -------------------------------------------------------------------------------- /37_kv/fixtures/client.cert: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIBXTCCAQ+gAwIBAgIJAKEchNUbV8e4MAUGAytlcDAzMQswCQYDVQQGDAJDTjES 3 | MBAGA1UECgwJQWNtZSBJbmMuMRAwDgYDVQQDDAdBY21lIENBMB4XDTIxMDkyNjAx 4 | MjU1OVoXDTIyMDkyNjAxMjU1OVowPTELMAkGA1UEBgwCQ04xEjAQBgNVBAoMCUFj 5 | bWUgSW5jLjEaMBgGA1UEAwwRYXdlc29tZS1kZXZpY2UtaWQwKjAFBgMrZXADIQDR 6 | 1n/nkKauN1RPwmK+gXe87WIXJw/QCHUOr4GEabT3qaM2MDQwEwYDVR0lBAwwCgYI 7 | KwYBBQUHAwIwDAYDVR0TBAUwAwEBADAPBgNVHQ8BAf8EBQMDB+AAMAUGAytlcANB 8 | APpIPMooTxwQA6aYP3C0ZeEouAq5FrbXihK0d3Wt+TTi8yUoH/miz5oHxYqGan+U 9 | 0EbIBKB+blgYwEPZ6u5Xcws= 10 | -----END CERTIFICATE----- 11 | -------------------------------------------------------------------------------- /37_kv/fixtures/client.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MFMCAQEwBQYDK2VwBCIEIHR35EEVaczWX7iBttg7LlOAYXzDsfRE1mOXJQjeryOR 3 | oSMDIQDR1n/nkKauN1RPwmK+gXe87WIXJw/QCHUOr4GEabT3qQ== 4 | -----END PRIVATE KEY----- 5 | -------------------------------------------------------------------------------- /37_kv/fixtures/server.cert: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIBdzCCASmgAwIBAgIICpy02U2yuPowBQYDK2VwMDMxCzAJBgNVBAYMAkNOMRIw 3 | EAYDVQQKDAlBY21lIEluYy4xEDAOBgNVBAMMB0FjbWUgQ0EwHhcNMjEwOTI2MDEy 4 | NTU5WhcNMjYwOTI1MDEyNTU5WjA6MQswCQYDVQQGDAJDTjESMBAGA1UECgwJQWNt 5 | ZSBJbmMuMRcwFQYDVQQDDA5BY21lIEtWIHNlcnZlcjAqMAUGAytlcAMhAK2Z2AjF 6 | A0uiltNuCvl6EVFl6tpaS/wJYB5IdWT2IISdo1QwUjAcBgNVHREEFTATghFrdnNl 7 | cnZlci5hY21lLmluYzATBgNVHSUEDDAKBggrBgEFBQcDATAMBgNVHRMEBTADAQEA 8 | MA8GA1UdDwEB/wQFAwMH4AAwBQYDK2VwA0EASGOmOWFPjbGhXNOmYNCa3lInbgRy 9 | iTNtB/5kElnbKkhKhRU7yQ8HTHWWkyU5WGWbOOIXEtYp+5ERUJC+mzP9Bw== 10 | -----END CERTIFICATE----- 11 | -------------------------------------------------------------------------------- /37_kv/fixtures/server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MFMCAQEwBQYDK2VwBCIEIPMyINaewhXwuTPUufFO2mMt/MvQMHrGDGxgdgfy/kUu 3 | oSMDIQCtmdgIxQNLopbTbgr5ehFRZeraWkv8CWAeSHVk9iCEnQ== 4 | -----END PRIVATE KEY----- 5 | -------------------------------------------------------------------------------- /37_kv/src/client.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use kv3::{CommandRequest, ProstClientStream, TlsClientConnector}; 3 | use tokio::net::TcpStream; 4 | use tracing::info; 5 | 6 | #[tokio::main] 7 | async fn main() -> Result<()> { 8 | tracing_subscriber::fmt::init(); 9 | 10 | // 以后用配置替换 11 | let ca_cert = include_str!("../fixtures/ca.cert"); 12 | 13 | let addr = "127.0.0.1:9527"; 14 | // 连接服务器 15 | let connector = TlsClientConnector::new("kvserver.acme.inc", None, Some(ca_cert))?; 16 | let stream = TcpStream::connect(addr).await?; 17 | let stream = connector.connect(stream).await?; 18 | 19 | let mut client = ProstClientStream::new(stream); 20 | 21 | // 生成一个 HSET 命令 22 | let cmd = CommandRequest::new_hset("table1", "hello", "world".to_string().into()); 23 | 24 | // 发送 HSET 命令 25 | let data = client.execute(cmd).await?; 26 | info!("Got response {:?}", data); 27 | 28 | Ok(()) 29 | } 30 | -------------------------------------------------------------------------------- /37_kv/src/error.rs: -------------------------------------------------------------------------------- 1 | use crate::Value; 2 | use thiserror::Error; 3 | 4 | #[derive(Error, Debug)] 5 | pub enum KvError { 6 | #[error("Not found for table: {0}, key: {1}")] 7 | NotFound(String, String), 8 | #[error("Frame is larger than max size")] 9 | FrameError, 10 | #[error("Command is invalid: `{0}`")] 11 | InvalidCommand(String), 12 | #[error("Cannot convert value {:0} to {1}")] 13 | ConvertError(Value, &'static str), 14 | #[error("Cannot process command {0} with table: {1}, key: {2}. Error: {}")] 15 | StorageError(&'static str, String, String, String), 16 | #[error("Certificate parse error: error to load {0} {0}")] 17 | CertifcateParseError(&'static str, &'static str), 18 | 19 | #[error("Failed to encode protobuf message")] 20 | EncodeError(#[from] prost::EncodeError), 21 | #[error("Failed to decode protobuf message")] 22 | DecodeError(#[from] prost::DecodeError), 23 | #[error("Failed to access sled db")] 24 | SledError(#[from] sled::Error), 25 | #[error("I/O error")] 26 | IoError(#[from] std::io::Error), 27 | #[error("TLS error")] 28 | TlsError(#[from] tokio_rustls::rustls::TLSError), 29 | 30 | #[error("Internal error: {0}")] 31 | Internal(String), 32 | } 33 | -------------------------------------------------------------------------------- /37_kv/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod error; 2 | mod network; 3 | mod pb; 4 | mod service; 5 | mod storage; 6 | 7 | pub use error::KvError; 8 | pub use network::*; 9 | pub use pb::abi::*; 10 | pub use service::*; 11 | pub use storage::*; 12 | -------------------------------------------------------------------------------- /37_kv/src/server.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use kv3::{MemTable, ProstServerStream, Service, ServiceInner, TlsServerAcceptor}; 3 | use tokio::net::TcpListener; 4 | use tracing::info; 5 | 6 | #[tokio::main] 7 | async fn main() -> Result<()> { 8 | tracing_subscriber::fmt::init(); 9 | let addr = "127.0.0.1:9527"; 10 | 11 | // 以后从配置文件取 12 | let server_cert = include_str!("../fixtures/server.cert"); 13 | let server_key = include_str!("../fixtures/server.key"); 14 | 15 | let acceptor = TlsServerAcceptor::new(server_cert, server_key, None)?; 16 | let service: Service = ServiceInner::new(MemTable::new()).into(); 17 | let listener = TcpListener::bind(addr).await?; 18 | info!("Start listening on {}", addr); 19 | loop { 20 | let tls = acceptor.clone(); 21 | let (stream, addr) = listener.accept().await?; 22 | info!("Client {:?} connected", addr); 23 | let stream = tls.accept(stream).await?; 24 | let stream = ProstServerStream::new(stream, service.clone()); 25 | tokio::spawn(async move { stream.process().await }); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /38_async/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "asynchronous" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dev-dependencies] 9 | anyhow = "1" 10 | blake3 = "1" 11 | futures = "0.3" 12 | rayon = "1" 13 | serde = { version = "1", features = ["derive"] } 14 | serde_yaml = "0.8" 15 | tokio = { version = "1", features = ["full"] } 16 | tokio-util = {version = "0.6", features = ["codec"] } 17 | toml = "0.5" 18 | -------------------------------------------------------------------------------- /38_async/examples/async_io.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use serde_yaml::Value; 3 | use tokio::{fs, try_join}; 4 | 5 | #[tokio::main] 6 | async fn main() -> Result<()> { 7 | // 读取 Cargo.toml,IO 操作 1 8 | let f1 = fs::read_to_string("./Cargo.toml"); 9 | // 读取 Cargo.lock,IO 操作 2 10 | let f2 = fs::read_to_string("./Cargo.lock"); 11 | let (content1, content2) = try_join!(f1, f2)?; 12 | 13 | // 计算 14 | let yaml1 = toml2yaml(&content1)?; 15 | let yaml2 = toml2yaml(&content2)?; 16 | 17 | // 写入 /tmp/Cargo.yml,IO 操作 3 18 | let f3 = fs::write("/tmp/Cargo.yml", &yaml1); 19 | // 写入 /tmp/Cargo.lock,IO 操作 4 20 | let f4 = fs::write("/tmp/Cargo.lock", &yaml2); 21 | try_join!(f3, f4)?; 22 | 23 | // 打印 24 | println!("{}", yaml1); 25 | println!("{}", yaml2); 26 | 27 | Ok(()) 28 | } 29 | 30 | fn toml2yaml(content: &str) -> Result { 31 | let value: Value = toml::from_str(content)?; 32 | Ok(serde_yaml::to_string(&value)?) 33 | } 34 | -------------------------------------------------------------------------------- /38_async/examples/async_test.rs: -------------------------------------------------------------------------------- 1 | use futures::executor::block_on; 2 | use std::future::Future; 3 | 4 | #[tokio::main] 5 | async fn main() { 6 | let name1 = "Tyr".to_string(); 7 | let name2 = "Lindsey".to_string(); 8 | 9 | say_hello1(&name1).await; 10 | say_hello2(&name2).await; 11 | 12 | // Future 除了可以用 await 来执行外,还可以用 executor 执行 13 | block_on(say_hello1(&name1)); 14 | block_on(say_hello2(&name2)); 15 | } 16 | 17 | async fn say_hello1(name: &str) -> usize { 18 | println!("Hello {}", name); 19 | 42 20 | } 21 | 22 | // async fn 关键字相当于一个返回 impl Future 的语法糖 23 | #[allow(clippy::all)] 24 | fn say_hello2<'fut>(name: &'fut str) -> impl Future + 'fut { 25 | async move { 26 | println!("Hello {}", name); 27 | 42 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /38_async/examples/async_thread.rs: -------------------------------------------------------------------------------- 1 | use futures::{executor::block_on, Future}; 2 | use std::thread::{self, JoinHandle}; 3 | 4 | fn main() { 5 | // thread 可以处理异步任务 6 | let t1 = thread_async(); 7 | // Future 可以处理异步任务 8 | let t2 = task_async(); 9 | 10 | // 线程一旦启动,就开始执行,这里的 join 不过是等待结果 11 | let r1 = t1.join().unwrap(); 12 | // Future 需要显式地 await 才能开始执行 13 | let r2 = block_on(async move { t2.await }); 14 | 15 | assert_eq!(r1, r2); 16 | } 17 | 18 | fn thread_async() -> JoinHandle { 19 | thread::spawn(move || { 20 | println!("hello thread!"); 21 | 42 22 | }) 23 | } 24 | 25 | #[allow(clippy::all)] 26 | fn task_async() -> impl Future { 27 | async { 28 | println!("hello async!"); 29 | 42 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /38_async/examples/blocking_async_task.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use std::time::Duration; 3 | 4 | #[allow(clippy::all)] 5 | #[tokio::main(worker_threads = 1)] 6 | async fn main() -> Result<()> { 7 | // 先开始执行 task 1 的话会阻塞,让 task 2 没有机会运行 8 | tokio::spawn(async move { 9 | eprintln!("task 1"); 10 | // 试试把这句注释掉看看会产生什么结果 11 | // tokio::time::sleep(Duration::from_millis(1)).await; 12 | loop {} 13 | }); 14 | 15 | tokio::spawn(async move { 16 | eprintln!("task 2"); 17 | }); 18 | 19 | tokio::time::sleep(Duration::from_millis(1)).await; 20 | Ok(()) 21 | } 22 | -------------------------------------------------------------------------------- /38_async/examples/mutex.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use std::{sync::Arc, time::Duration}; 3 | use tokio::sync::Mutex; 4 | 5 | struct DB; 6 | 7 | impl DB { 8 | // 假装在 commit 数据 9 | async fn commit(&mut self) -> Result { 10 | Ok(42) 11 | } 12 | } 13 | 14 | #[tokio::main] 15 | async fn main() -> Result<()> { 16 | let db1 = Arc::new(Mutex::new(DB)); 17 | let db2 = Arc::clone(&db1); 18 | 19 | tokio::spawn(async move { 20 | let mut db = db1.lock().await; 21 | // 因为拿到的 MutexGuard 要跨越 await,所以不能用 std::sync::Mutex 22 | // 只能用 tokio::sync::Mutex 23 | let affected = db.commit().await?; 24 | println!("db1: Total affected rows: {}", affected); 25 | Ok::<_, anyhow::Error>(()) 26 | }); 27 | 28 | tokio::spawn(async move { 29 | let mut db = db2.lock().await; 30 | let affected = db.commit().await?; 31 | println!("db2: Total affected rows: {}", affected); 32 | 33 | Ok::<_, anyhow::Error>(()) 34 | }); 35 | 36 | // 让两个 task 有机会执行完 37 | tokio::time::sleep(Duration::from_millis(1)).await; 38 | 39 | Ok(()) 40 | } 41 | -------------------------------------------------------------------------------- /38_async/examples/sync_io.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use serde_yaml::Value; 3 | use std::fs; 4 | 5 | fn main() -> Result<()> { 6 | // 读取 Cargo.toml,IO 操作 1 7 | let content1 = fs::read_to_string("./Cargo.toml")?; 8 | // 读取 Cargo.lock,IO 操作 2 9 | let content2 = fs::read_to_string("./Cargo.lock")?; 10 | 11 | // 计算 12 | let yaml1 = toml2yaml(&content1)?; 13 | let yaml2 = toml2yaml(&content2)?; 14 | 15 | // 写入 /tmp/Cargo.yml,IO 操作 3 16 | fs::write("/tmp/Cargo.yml", &yaml1)?; 17 | // 写入 /tmp/Cargo.lock,IO 操作 4 18 | fs::write("/tmp/Cargo.lock", &yaml2)?; 19 | 20 | // 打印 21 | println!("{}", yaml1); 22 | println!("{}", yaml2); 23 | 24 | Ok(()) 25 | } 26 | 27 | fn toml2yaml(content: &str) -> Result { 28 | let value: Value = toml::from_str(content)?; 29 | Ok(serde_yaml::to_string(&value)?) 30 | } 31 | -------------------------------------------------------------------------------- /38_async/examples/tcp_listener.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use futures::{SinkExt, StreamExt}; 3 | use tokio::net::TcpListener; 4 | use tokio_util::codec::{Framed, LinesCodec}; 5 | 6 | #[tokio::main] 7 | async fn main() -> Result<()> { 8 | let addr = "0.0.0.0:8080"; 9 | let listener = TcpListener::bind(addr).await?; 10 | println!("listen to: {}", addr); 11 | loop { 12 | let (stream, addr) = listener.accept().await?; 13 | println!("Accepted: {:?}", addr); 14 | tokio::spawn(async move { 15 | // 使用 LinesCodec 把 TCP 数据切成一行行字符串处理 16 | let framed = Framed::new(stream, LinesCodec::new()); 17 | // split 成 writer 和 reader 18 | let (mut w, mut r) = framed.split(); 19 | while let Some(Ok(line)) = r.next().await { 20 | // 每读到一行就加个前缀发回 21 | w.send(format!("I got: {}", line)).await?; 22 | } 23 | Ok::<_, anyhow::Error>(()) 24 | }); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /38_async/src/lib.rs: -------------------------------------------------------------------------------- 1 | // see examples 2 | -------------------------------------------------------------------------------- /39_async_internals/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "async_internals" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | 10 | [dev-dependencies] 11 | anyhow = "1" 12 | futures = "0.3" 13 | pin-project = "1" 14 | tokio = { version = "1", features = ["full"] } 15 | -------------------------------------------------------------------------------- /39_async_internals/examples/async_type.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let fut = async { 42 }; 3 | 4 | println!("type of fut is: {}", get_type_name(&fut)); 5 | println!("type of hello fut is: {}", get_type_name(&hello("Tyr"))); 6 | } 7 | 8 | fn get_type_name(_: &T) -> &'static str { 9 | std::any::type_name::() 10 | } 11 | 12 | async fn hello(name: &str) -> String { 13 | format!("hello {}", name) 14 | } 15 | -------------------------------------------------------------------------------- /39_async_internals/examples/self_ref1.rs: -------------------------------------------------------------------------------- 1 | struct SelfReference<'a> { 2 | name: String, 3 | name_ref: Option<&'a str>, 4 | } 5 | 6 | fn main() { 7 | let mut data = SelfReference { 8 | name: "Tyr".into(), 9 | name_ref: None, 10 | }; 11 | data.name_ref = Some(&data.name); 12 | 13 | // 如果 move,直接编译不过(std::mem::swap 也是如此) 14 | // move_it(data); 15 | } 16 | 17 | #[allow(dead_code)] 18 | fn move_it(data: SelfReference) -> SelfReference { 19 | data 20 | } 21 | -------------------------------------------------------------------------------- /39_async_internals/src/lib.rs: -------------------------------------------------------------------------------- 1 | // see examples 2 | -------------------------------------------------------------------------------- /40_async_stream/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "async_stream" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dev-dependencies] 9 | anyhow = "1" 10 | bytes = "1" 11 | futures = "0.3" 12 | pin-project = "1" 13 | tokio = { version = "1", features = ["full" ] } 14 | tokio-util = { version = "0.6", features = ["compat", "codec"] } 15 | tracing = "0.1" 16 | tracing-subscriber = "0.3" 17 | yamux = "0.9" 18 | -------------------------------------------------------------------------------- /40_async_stream/examples/sink.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use futures::prelude::*; 3 | use tokio::{fs::File, io::AsyncWriteExt}; 4 | 5 | #[tokio::main] 6 | async fn main() -> Result<()> { 7 | let file_sink = writer(File::create("/tmp/hello").await?); 8 | // pin_mut 可以把变量 pin 住 9 | futures::pin_mut!(file_sink); 10 | if file_sink.send("hello\n").await.is_err() { 11 | println!("Error on send"); 12 | } 13 | if file_sink.send("world!\n").await.is_err() { 14 | println!("Error on send"); 15 | } 16 | Ok(()) 17 | } 18 | 19 | /// 使用 unfold 生成一个 Sink 数据结构 20 | fn writer<'a>(file: File) -> impl Sink<&'a str> { 21 | sink::unfold(file, |mut file, line: &'a str| async move { 22 | file.write_all(line.as_bytes()).await?; 23 | eprint!("Received: {}", line); 24 | Ok::<_, std::io::Error>(file) 25 | }) 26 | } 27 | -------------------------------------------------------------------------------- /40_async_stream/examples/stream_iter.rs: -------------------------------------------------------------------------------- 1 | use futures::prelude::*; 2 | 3 | #[tokio::main] 4 | async fn main() { 5 | let mut st = stream::iter(1..10) 6 | .filter(|x| future::ready(x % 2 == 0)) 7 | .map(|x| x * x); 8 | 9 | println!("Type of stream: {}", get_type_name(&st)); 10 | while let Some(x) = st.next().await { 11 | println!("Got item: {}", x); 12 | } 13 | } 14 | 15 | fn get_type_name(_: &T) -> &'static str { 16 | std::any::type_name::() 17 | } 18 | -------------------------------------------------------------------------------- /40_async_stream/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | #[test] 4 | fn it_works() { 5 | let result = 2 + 2; 6 | assert_eq!(result, 4); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /41_kv/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/405-studio/geektime-rust/3c57ef418118eacc24d2e35acb470aa4e6a6a672/41_kv/.gitignore -------------------------------------------------------------------------------- /41_kv/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kv4" 3 | version = "0.1.0" 4 | edition = "2018" 5 | 6 | [[bin]] 7 | name = "kvs" 8 | path = "src/server.rs" 9 | doc = false 10 | 11 | [[bin]] 12 | name = "kvc" 13 | path = "src/client.rs" 14 | doc = false 15 | 16 | [dependencies] 17 | anyhow = "1" # 错误处理 18 | bytes = "1" # 高效处理网络 buffer 的库 19 | dashmap = "4" # 并发 HashMap 20 | flate2 = "1" # gzip 压缩 21 | futures = "0.3" # 提供 Stream trait 22 | http = "0.2" # 我们使用 HTTP status code 所以引入这个类型库 23 | prost = "0.8" # 处理 protobuf 的代码 24 | rustls-native-certs = "0.5" 25 | sled = "0.34" # sled db 26 | thiserror = "1" # 错误定义和处理 27 | tokio = { version = "1", features = ["full" ] } # 异步网络库 28 | tokio-rustls = "0.22" 29 | tracing = "0.1" # 日志处理 30 | tracing-subscriber = "0.2" # 日志处理 31 | 32 | [dev-dependencies] 33 | async-prost = "0.2.1" # 支持把 protobuf 封装成 TCP frame 34 | certify = "0.3" 35 | tempfile = "3" # 处理临时目录和临时文件 36 | tokio-util = { version = "0.6", features = ["codec"]} 37 | 38 | [build-dependencies] 39 | prost-build = "0.8" # 编译 protobuf 40 | -------------------------------------------------------------------------------- /41_kv/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let mut config = prost_build::Config::new(); 3 | config.bytes(&["."]); 4 | config.type_attribute(".", "#[derive(PartialOrd)]"); 5 | config 6 | .out_dir("src/pb") 7 | .compile_protos(&["abi.proto"], &["."]) 8 | .unwrap(); 9 | 10 | println!("cargo:rerun-if-changed=build.rs"); 11 | println!("cargo:rerun-if-changed=abi.proto"); 12 | } 13 | -------------------------------------------------------------------------------- /41_kv/examples/client.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use async_prost::AsyncProstStream; 3 | use futures::prelude::*; 4 | use kv4::{CommandRequest, CommandResponse}; 5 | use tokio::net::TcpStream; 6 | use tracing::info; 7 | 8 | #[tokio::main] 9 | async fn main() -> Result<()> { 10 | tracing_subscriber::fmt::init(); 11 | 12 | let addr = "127.0.0.1:9527"; 13 | // 连接服务器 14 | let stream = TcpStream::connect(addr).await?; 15 | 16 | // 使用 AsyncProstStream 来处理 TCP Frame 17 | let mut client = 18 | AsyncProstStream::<_, CommandResponse, CommandRequest, _>::from(stream).for_async(); 19 | 20 | // 生成一个 HSET 命令 21 | let cmd = CommandRequest::new_hset("table1", "hello", "world".to_string().into()); 22 | 23 | // 发送 HSET 命令 24 | client.send(cmd).await?; 25 | if let Some(Ok(data)) = client.next().await { 26 | info!("Got response {:?}", data); 27 | } 28 | 29 | Ok(()) 30 | } 31 | -------------------------------------------------------------------------------- /41_kv/examples/server.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use async_prost::AsyncProstStream; 3 | use futures::prelude::*; 4 | use kv4::{CommandRequest, CommandResponse, MemTable, Service, ServiceInner}; 5 | use tokio::net::TcpListener; 6 | use tracing::info; 7 | 8 | #[tokio::main] 9 | async fn main() -> Result<()> { 10 | tracing_subscriber::fmt::init(); 11 | let service: Service = ServiceInner::new(MemTable::new()).into(); 12 | let addr = "127.0.0.1:9527"; 13 | let listener = TcpListener::bind(addr).await?; 14 | info!("Start listening on {}", addr); 15 | loop { 16 | let (stream, addr) = listener.accept().await?; 17 | info!("Client {:?} connected", addr); 18 | let svc = service.clone(); 19 | tokio::spawn(async move { 20 | let mut stream = 21 | AsyncProstStream::<_, CommandRequest, CommandResponse, _>::from(stream).for_async(); 22 | while let Some(Ok(cmd)) = stream.next().await { 23 | info!("Got a new command: {:?}", cmd); 24 | let res = svc.execute(cmd); 25 | stream.send(res).await.unwrap(); 26 | } 27 | info!("Client {:?} disconnected", addr); 28 | }); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /41_kv/fixtures/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/405-studio/geektime-rust/3c57ef418118eacc24d2e35acb470aa4e6a6a672/41_kv/fixtures/.gitkeep -------------------------------------------------------------------------------- /41_kv/fixtures/ca.cert: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIBeDCCASqgAwIBAgIJAK/ACf4lHrMIMAUGAytlcDAzMQswCQYDVQQGDAJDTjES 3 | MBAGA1UECgwJQWNtZSBJbmMuMRAwDgYDVQQDDAdBY21lIENBMB4XDTIxMDkyNjAx 4 | MjU1OVoXDTMxMDkyNDAxMjU1OVowMzELMAkGA1UEBgwCQ04xEjAQBgNVBAoMCUFj 5 | bWUgSW5jLjEQMA4GA1UEAwwHQWNtZSBDQTAqMAUGAytlcAMhAO6QG6Ma8p4xEZ0V 6 | 9VUutcHGutlezoR4E3geBYVojMSKo1swWTATBgNVHREEDDAKgghhY21lLmluYzAd 7 | BgNVHQ4EFgQU8kevGAonItSdc8VGWa74jwYUnHgwEgYDVR0TAQH/BAgwBgEB/wIB 8 | EDAPBgNVHQ8BAf8EBQMDBwYAMAUGAytlcANBAIzbgTAiy6SHCQxfJhlQAs1dIYgU 9 | jwyjxoeYKv/jgGoJd9fMBXsD94tEtyOwF6ph6JEJh0SwCLZrQw2OIp/H7QU= 10 | -----END CERTIFICATE----- 11 | -------------------------------------------------------------------------------- /41_kv/fixtures/ca.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MFMCAQEwBQYDK2VwBCIEIDGUGlTZG5ZZ0NC/qfb+A96ofGsg4pPchKss2VodfQwf 3 | oSMDIQDukBujGvKeMRGdFfVVLrXBxrrZXs6EeBN4HgWFaIzEig== 4 | -----END PRIVATE KEY----- 5 | -------------------------------------------------------------------------------- /41_kv/fixtures/client.cert: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIBXTCCAQ+gAwIBAgIJAKEchNUbV8e4MAUGAytlcDAzMQswCQYDVQQGDAJDTjES 3 | MBAGA1UECgwJQWNtZSBJbmMuMRAwDgYDVQQDDAdBY21lIENBMB4XDTIxMDkyNjAx 4 | MjU1OVoXDTIyMDkyNjAxMjU1OVowPTELMAkGA1UEBgwCQ04xEjAQBgNVBAoMCUFj 5 | bWUgSW5jLjEaMBgGA1UEAwwRYXdlc29tZS1kZXZpY2UtaWQwKjAFBgMrZXADIQDR 6 | 1n/nkKauN1RPwmK+gXe87WIXJw/QCHUOr4GEabT3qaM2MDQwEwYDVR0lBAwwCgYI 7 | KwYBBQUHAwIwDAYDVR0TBAUwAwEBADAPBgNVHQ8BAf8EBQMDB+AAMAUGAytlcANB 8 | APpIPMooTxwQA6aYP3C0ZeEouAq5FrbXihK0d3Wt+TTi8yUoH/miz5oHxYqGan+U 9 | 0EbIBKB+blgYwEPZ6u5Xcws= 10 | -----END CERTIFICATE----- 11 | -------------------------------------------------------------------------------- /41_kv/fixtures/client.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MFMCAQEwBQYDK2VwBCIEIHR35EEVaczWX7iBttg7LlOAYXzDsfRE1mOXJQjeryOR 3 | oSMDIQDR1n/nkKauN1RPwmK+gXe87WIXJw/QCHUOr4GEabT3qQ== 4 | -----END PRIVATE KEY----- 5 | -------------------------------------------------------------------------------- /41_kv/fixtures/server.cert: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIBdzCCASmgAwIBAgIICpy02U2yuPowBQYDK2VwMDMxCzAJBgNVBAYMAkNOMRIw 3 | EAYDVQQKDAlBY21lIEluYy4xEDAOBgNVBAMMB0FjbWUgQ0EwHhcNMjEwOTI2MDEy 4 | NTU5WhcNMjYwOTI1MDEyNTU5WjA6MQswCQYDVQQGDAJDTjESMBAGA1UECgwJQWNt 5 | ZSBJbmMuMRcwFQYDVQQDDA5BY21lIEtWIHNlcnZlcjAqMAUGAytlcAMhAK2Z2AjF 6 | A0uiltNuCvl6EVFl6tpaS/wJYB5IdWT2IISdo1QwUjAcBgNVHREEFTATghFrdnNl 7 | cnZlci5hY21lLmluYzATBgNVHSUEDDAKBggrBgEFBQcDATAMBgNVHRMEBTADAQEA 8 | MA8GA1UdDwEB/wQFAwMH4AAwBQYDK2VwA0EASGOmOWFPjbGhXNOmYNCa3lInbgRy 9 | iTNtB/5kElnbKkhKhRU7yQ8HTHWWkyU5WGWbOOIXEtYp+5ERUJC+mzP9Bw== 10 | -----END CERTIFICATE----- 11 | -------------------------------------------------------------------------------- /41_kv/fixtures/server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MFMCAQEwBQYDK2VwBCIEIPMyINaewhXwuTPUufFO2mMt/MvQMHrGDGxgdgfy/kUu 3 | oSMDIQCtmdgIxQNLopbTbgr5ehFRZeraWkv8CWAeSHVk9iCEnQ== 4 | -----END PRIVATE KEY----- 5 | -------------------------------------------------------------------------------- /41_kv/src/client.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use kv4::{CommandRequest, ProstClientStream, TlsClientConnector}; 3 | use tokio::net::TcpStream; 4 | use tracing::info; 5 | 6 | #[tokio::main] 7 | async fn main() -> Result<()> { 8 | tracing_subscriber::fmt::init(); 9 | 10 | // 以后用配置替换 11 | let ca_cert = include_str!("../fixtures/ca.cert"); 12 | 13 | let addr = "127.0.0.1:9527"; 14 | // 连接服务器 15 | let connector = TlsClientConnector::new("kvserver.acme.inc", None, Some(ca_cert))?; 16 | let stream = TcpStream::connect(addr).await?; 17 | let stream = connector.connect(stream).await?; 18 | 19 | let mut client = ProstClientStream::new(stream); 20 | 21 | // 生成一个 HSET 命令 22 | let cmd = CommandRequest::new_hset("table1", "hello", "world".to_string().into()); 23 | 24 | // 发送 HSET 命令 25 | let data = client.execute(cmd).await?; 26 | info!("Got response {:?}", data); 27 | 28 | Ok(()) 29 | } 30 | -------------------------------------------------------------------------------- /41_kv/src/error.rs: -------------------------------------------------------------------------------- 1 | use crate::Value; 2 | use thiserror::Error; 3 | 4 | #[derive(Error, Debug)] 5 | pub enum KvError { 6 | #[error("Not found for table: {0}, key: {1}")] 7 | NotFound(String, String), 8 | #[error("Frame is larger than max size")] 9 | FrameError, 10 | #[error("Command is invalid: `{0}`")] 11 | InvalidCommand(String), 12 | #[error("Cannot convert value {:0} to {1}")] 13 | ConvertError(Value, &'static str), 14 | #[error("Cannot process command {0} with table: {1}, key: {2}. Error: {}")] 15 | StorageError(&'static str, String, String, String), 16 | #[error("Certificate parse error: error to load {0} {0}")] 17 | CertifcateParseError(&'static str, &'static str), 18 | 19 | #[error("Failed to encode protobuf message")] 20 | EncodeError(#[from] prost::EncodeError), 21 | #[error("Failed to decode protobuf message")] 22 | DecodeError(#[from] prost::DecodeError), 23 | #[error("Failed to access sled db")] 24 | SledError(#[from] sled::Error), 25 | #[error("I/O error")] 26 | IoError(#[from] std::io::Error), 27 | #[error("TLS error")] 28 | TlsError(#[from] tokio_rustls::rustls::TLSError), 29 | 30 | #[error("Internal error: {0}")] 31 | Internal(String), 32 | } 33 | -------------------------------------------------------------------------------- /41_kv/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod error; 2 | mod network; 3 | mod pb; 4 | mod service; 5 | mod storage; 6 | 7 | pub use error::KvError; 8 | pub use network::*; 9 | pub use pb::abi::*; 10 | pub use service::*; 11 | pub use storage::*; 12 | -------------------------------------------------------------------------------- /41_kv/src/server.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use kv4::{MemTable, ProstServerStream, Service, ServiceInner, TlsServerAcceptor}; 3 | use tokio::net::TcpListener; 4 | use tracing::info; 5 | 6 | #[tokio::main] 7 | async fn main() -> Result<()> { 8 | tracing_subscriber::fmt::init(); 9 | let addr = "127.0.0.1:9527"; 10 | 11 | // 以后从配置文件取 12 | let server_cert = include_str!("../fixtures/server.cert"); 13 | let server_key = include_str!("../fixtures/server.key"); 14 | 15 | let acceptor = TlsServerAcceptor::new(server_cert, server_key, None)?; 16 | let service: Service = ServiceInner::new(MemTable::new()).into(); 17 | let listener = TcpListener::bind(addr).await?; 18 | info!("Start listening on {}", addr); 19 | loop { 20 | let tls = acceptor.clone(); 21 | let (stream, addr) = listener.accept().await?; 22 | info!("Client {:?} connected", addr); 23 | let stream = tls.accept(stream).await?; 24 | let stream = ProstServerStream::new(stream, service.clone()); 25 | tokio::spawn(async move { stream.process().await }); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /42_kv/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/405-studio/geektime-rust/3c57ef418118eacc24d2e35acb470aa4e6a6a672/42_kv/.gitignore -------------------------------------------------------------------------------- /42_kv/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kv5" 3 | version = "0.1.0" 4 | edition = "2018" 5 | 6 | [[bin]] 7 | name = "kvs" 8 | path = "src/server.rs" 9 | doc = false 10 | 11 | [[bin]] 12 | name = "kvc" 13 | path = "src/client.rs" 14 | doc = false 15 | 16 | [dependencies] 17 | anyhow = "1" # 错误处理 18 | bytes = "1" # 高效处理网络 buffer 的库 19 | dashmap = "4" # 并发 HashMap 20 | flate2 = "1" # gzip 压缩 21 | futures = "0.3" # 提供 Stream trait 22 | http = "0.2" # 我们使用 HTTP status code 所以引入这个类型库 23 | prost = "0.8" # 处理 protobuf 的代码 24 | rustls-native-certs = "0.5" 25 | sled = "0.34" # sled db 26 | thiserror = "1" # 错误定义和处理 27 | tokio = { version = "1", features = ["full" ] } # 异步网络库 28 | tokio-rustls = "0.22" # 处理 TLS 29 | tokio-stream = { version = "0.1", features = ["sync"] } # 处理 stream 30 | tokio-util = { version = "0.6", features = ["compat"]} # tokio 和 futures 的兼容性库 31 | tracing = "0.1" # 日志处理 32 | tracing-subscriber = "0.2" # 日志处理 33 | yamux = "0.9" # yamux 多路复用支持 34 | 35 | [dev-dependencies] 36 | async-prost = "0.2.1" # 支持把 protobuf 封装成 TCP frame 37 | certify = "0.3" 38 | tempfile = "3" # 处理临时目录和临时文件 39 | tokio-util = { version = "0.6", features = ["codec"]} 40 | 41 | [build-dependencies] 42 | prost-build = "0.8" # 编译 protobuf 43 | -------------------------------------------------------------------------------- /42_kv/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let mut config = prost_build::Config::new(); 3 | config.bytes(&["."]); 4 | config.type_attribute(".", "#[derive(PartialOrd)]"); 5 | config 6 | .out_dir("src/pb") 7 | .compile_protos(&["abi.proto"], &["."]) 8 | .unwrap(); 9 | 10 | println!("cargo:rerun-if-changed=build.rs"); 11 | println!("cargo:rerun-if-changed=abi.proto"); 12 | } 13 | -------------------------------------------------------------------------------- /42_kv/examples/client.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use async_prost::AsyncProstStream; 3 | use futures::prelude::*; 4 | use kv5::{CommandRequest, CommandResponse}; 5 | use tokio::net::TcpStream; 6 | use tracing::info; 7 | 8 | #[tokio::main] 9 | async fn main() -> Result<()> { 10 | tracing_subscriber::fmt::init(); 11 | 12 | let addr = "127.0.0.1:9527"; 13 | // 连接服务器 14 | let stream = TcpStream::connect(addr).await?; 15 | 16 | // 使用 AsyncProstStream 来处理 TCP Frame 17 | let mut client = 18 | AsyncProstStream::<_, CommandResponse, CommandRequest, _>::from(stream).for_async(); 19 | 20 | // 生成一个 HSET 命令 21 | let cmd = CommandRequest::new_hset("table1", "hello", "world".to_string().into()); 22 | 23 | // 发送 HSET 命令 24 | client.send(cmd).await?; 25 | if let Some(Ok(data)) = client.next().await { 26 | info!("Got response {:?}", data); 27 | } 28 | 29 | Ok(()) 30 | } 31 | -------------------------------------------------------------------------------- /42_kv/fixtures/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/405-studio/geektime-rust/3c57ef418118eacc24d2e35acb470aa4e6a6a672/42_kv/fixtures/.gitkeep -------------------------------------------------------------------------------- /42_kv/fixtures/ca.cert: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIBeDCCASqgAwIBAgIJAK/ACf4lHrMIMAUGAytlcDAzMQswCQYDVQQGDAJDTjES 3 | MBAGA1UECgwJQWNtZSBJbmMuMRAwDgYDVQQDDAdBY21lIENBMB4XDTIxMDkyNjAx 4 | MjU1OVoXDTMxMDkyNDAxMjU1OVowMzELMAkGA1UEBgwCQ04xEjAQBgNVBAoMCUFj 5 | bWUgSW5jLjEQMA4GA1UEAwwHQWNtZSBDQTAqMAUGAytlcAMhAO6QG6Ma8p4xEZ0V 6 | 9VUutcHGutlezoR4E3geBYVojMSKo1swWTATBgNVHREEDDAKgghhY21lLmluYzAd 7 | BgNVHQ4EFgQU8kevGAonItSdc8VGWa74jwYUnHgwEgYDVR0TAQH/BAgwBgEB/wIB 8 | EDAPBgNVHQ8BAf8EBQMDBwYAMAUGAytlcANBAIzbgTAiy6SHCQxfJhlQAs1dIYgU 9 | jwyjxoeYKv/jgGoJd9fMBXsD94tEtyOwF6ph6JEJh0SwCLZrQw2OIp/H7QU= 10 | -----END CERTIFICATE----- 11 | -------------------------------------------------------------------------------- /42_kv/fixtures/ca.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MFMCAQEwBQYDK2VwBCIEIDGUGlTZG5ZZ0NC/qfb+A96ofGsg4pPchKss2VodfQwf 3 | oSMDIQDukBujGvKeMRGdFfVVLrXBxrrZXs6EeBN4HgWFaIzEig== 4 | -----END PRIVATE KEY----- 5 | -------------------------------------------------------------------------------- /42_kv/fixtures/client.cert: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIBXTCCAQ+gAwIBAgIJAKEchNUbV8e4MAUGAytlcDAzMQswCQYDVQQGDAJDTjES 3 | MBAGA1UECgwJQWNtZSBJbmMuMRAwDgYDVQQDDAdBY21lIENBMB4XDTIxMDkyNjAx 4 | MjU1OVoXDTIyMDkyNjAxMjU1OVowPTELMAkGA1UEBgwCQ04xEjAQBgNVBAoMCUFj 5 | bWUgSW5jLjEaMBgGA1UEAwwRYXdlc29tZS1kZXZpY2UtaWQwKjAFBgMrZXADIQDR 6 | 1n/nkKauN1RPwmK+gXe87WIXJw/QCHUOr4GEabT3qaM2MDQwEwYDVR0lBAwwCgYI 7 | KwYBBQUHAwIwDAYDVR0TBAUwAwEBADAPBgNVHQ8BAf8EBQMDB+AAMAUGAytlcANB 8 | APpIPMooTxwQA6aYP3C0ZeEouAq5FrbXihK0d3Wt+TTi8yUoH/miz5oHxYqGan+U 9 | 0EbIBKB+blgYwEPZ6u5Xcws= 10 | -----END CERTIFICATE----- 11 | -------------------------------------------------------------------------------- /42_kv/fixtures/client.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MFMCAQEwBQYDK2VwBCIEIHR35EEVaczWX7iBttg7LlOAYXzDsfRE1mOXJQjeryOR 3 | oSMDIQDR1n/nkKauN1RPwmK+gXe87WIXJw/QCHUOr4GEabT3qQ== 4 | -----END PRIVATE KEY----- 5 | -------------------------------------------------------------------------------- /42_kv/fixtures/server.cert: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIBdzCCASmgAwIBAgIICpy02U2yuPowBQYDK2VwMDMxCzAJBgNVBAYMAkNOMRIw 3 | EAYDVQQKDAlBY21lIEluYy4xEDAOBgNVBAMMB0FjbWUgQ0EwHhcNMjEwOTI2MDEy 4 | NTU5WhcNMjYwOTI1MDEyNTU5WjA6MQswCQYDVQQGDAJDTjESMBAGA1UECgwJQWNt 5 | ZSBJbmMuMRcwFQYDVQQDDA5BY21lIEtWIHNlcnZlcjAqMAUGAytlcAMhAK2Z2AjF 6 | A0uiltNuCvl6EVFl6tpaS/wJYB5IdWT2IISdo1QwUjAcBgNVHREEFTATghFrdnNl 7 | cnZlci5hY21lLmluYzATBgNVHSUEDDAKBggrBgEFBQcDATAMBgNVHRMEBTADAQEA 8 | MA8GA1UdDwEB/wQFAwMH4AAwBQYDK2VwA0EASGOmOWFPjbGhXNOmYNCa3lInbgRy 9 | iTNtB/5kElnbKkhKhRU7yQ8HTHWWkyU5WGWbOOIXEtYp+5ERUJC+mzP9Bw== 10 | -----END CERTIFICATE----- 11 | -------------------------------------------------------------------------------- /42_kv/fixtures/server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MFMCAQEwBQYDK2VwBCIEIPMyINaewhXwuTPUufFO2mMt/MvQMHrGDGxgdgfy/kUu 3 | oSMDIQCtmdgIxQNLopbTbgr5ehFRZeraWkv8CWAeSHVk9iCEnQ== 4 | -----END PRIVATE KEY----- 5 | -------------------------------------------------------------------------------- /42_kv/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod error; 2 | mod network; 3 | mod pb; 4 | mod service; 5 | mod storage; 6 | 7 | pub use error::KvError; 8 | pub use network::*; 9 | pub use pb::abi::*; 10 | pub use service::*; 11 | pub use storage::*; 12 | -------------------------------------------------------------------------------- /43_docdoc/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "docdoc" 3 | version = "0.1.0" 4 | edition = "2021" 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" 10 | -------------------------------------------------------------------------------- /43_docdoc/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![deny(missing_docs, rustdoc::missing_doc_code_examples)] 2 | 3 | //! 这是 crate 文档 4 | 5 | use std::task::Poll; 6 | 7 | use futures::{prelude::*, stream::poll_fn}; 8 | 9 | /// fibnacci 算法 10 | /// 示例: 11 | /// ``` 12 | /// use futures::prelude::*; 13 | /// use docdoc::fib; 14 | /// # futures::executor::block_on(async { 15 | /// let mut st = fib(10); 16 | /// assert_eq!(Some(2), st.next().await); 17 | /// # }); 18 | /// ``` 19 | pub fn fib(mut n: usize) -> impl Stream { 20 | let mut a = 1; 21 | let mut b = 1; 22 | poll_fn(move |_cx| -> Poll> { 23 | if n == 0 { 24 | return Poll::Ready(None); 25 | } 26 | n -= 1; 27 | let c = a + b; 28 | a = b; 29 | b = c; 30 | Poll::Ready(Some(b)) 31 | }) 32 | } 33 | 34 | /// 写入文件 35 | /// 示例: 36 | /// ``` 37 | /// use docdoc::write_file; 38 | /// write_file("/tmp/dummy_test", "hello world")?; 39 | /// # Ok::<_, std::io::Error>(()) 40 | /// ``` 41 | pub fn write_file(name: &str, contents: &str) -> Result<(), std::io::Error> { 42 | std::fs::write(name, contents) 43 | } 44 | -------------------------------------------------------------------------------- /44_data_processing/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "data_processing" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dev-dependencies] 9 | anyhow = "1" 10 | argon2 = "0.3" 11 | datafusion = "6" 12 | lazy_static = "1" 13 | rand_core = { version = "0.6", features = ["std"] } 14 | serde = { version = "1", features = ["derive"] } 15 | serde_yaml = "0.8" 16 | sqlx = { version = "0.5", features = ["runtime-tokio-rustls", "sqlite"] } 17 | tokio = { version = "1", features = ["full" ] } 18 | -------------------------------------------------------------------------------- /44_data_processing/data/.gitignore: -------------------------------------------------------------------------------- 1 | *.db-shm 2 | *.db-wal 3 | -------------------------------------------------------------------------------- /44_data_processing/data/create_users.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS users 2 | ( 3 | id INTEGER PRIMARY KEY NOT NULL, 4 | email VARCHAR UNIQUE NOT NULL, 5 | hashed_password VARCHAR NOT NULL 6 | ); 7 | -------------------------------------------------------------------------------- /44_data_processing/data/example.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/405-studio/geektime-rust/3c57ef418118eacc24d2e35acb470aa4e6a6a672/44_data_processing/data/example.db -------------------------------------------------------------------------------- /44_data_processing/fixtures/analyze.sql: -------------------------------------------------------------------------------- 1 | -- 查询 ip 前 10 名 2 | SELECT ip, count(*) as total, cast(avg(len) as int) as avg_len FROM nginx GROUP BY ip ORDER BY total DESC LIMIT 10 3 | -- 查询 UA 前 10 名 4 | select ua, count(*) as total from nginx group by ua order by total desc limit 10 5 | -- 查询访问最多的 url 前 10 名 6 | select url, count(*) as total from nginx group by url order by total desc limit 10 7 | -- 查询访问返回 body 长度前 10 名 8 | select len, count(*) as total from nginx group by len order by total desc limit 10 9 | -- 查询 HEAD 请求 10 | select ip, date, url, code, ua from nginx where method = 'HEAD' limit 10 11 | -- 查询状态码是 403 的请求 12 | select ip, date, url, ua from nginx where code = 403 limit 10 13 | -- 查询 UA 为空的请求 14 | select ip, date, url, code from nginx where ua = '-' limit 10 15 | -- 复杂查询,找返回 body 长度的 percentile 在 0.5-0.7 之间的数据 16 | select * from (select ip, date, url, ua, len, PERCENT_RANK() OVER (ORDER BY len) as len_percentile from nginx where code = 200 order by len desc) as t where t.len_percentile > 0.5 and t.len_percentile < 0.7 order by t.len_percentile desc limit 10 17 | -------------------------------------------------------------------------------- /44_data_processing/fixtures/log_schema.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: ip 3 | type: string 4 | - name: unused1 5 | type: string 6 | - name: unused2 7 | type: string 8 | - name: date 9 | type: string 10 | - name: method 11 | type: string 12 | - name: url 13 | type: string 14 | - name: version 15 | type: string 16 | - name: code 17 | type: integer 18 | - name: len 19 | type: integer 20 | - name: unused3 21 | type: string 22 | - name: ua 23 | type: string 24 | -------------------------------------------------------------------------------- /44_data_processing/fixtures/logs.csv: -------------------------------------------------------------------------------- 1 | 93.180.71.3 - - "17/May/2015:08:05:32 +0000" GET "/downloads/product_1" "HTTP/1.1" 304 0 "-" "Debian APT-HTTP/1.3 (0.8.16~exp12ubuntu10.21)" 2 | 93.180.71.3 - - "17/May/2015:08:05:23 +0000" GET "/downloads/product_1" "HTTP/1.1" 304 0 "-" "Debian APT-HTTP/1.3 (0.8.16~exp12ubuntu10.21)" 3 | 80.91.33.133 - - "17/May/2015:08:05:24 +0000" GET "/downloads/product_1" "HTTP/1.1" 304 0 "-" "Debian APT-HTTP/1.3 (0.8.16~exp12ubuntu10.17)" 4 | -------------------------------------------------------------------------------- /44_data_processing/src/lib.rs: -------------------------------------------------------------------------------- 1 | // see examples 2 | -------------------------------------------------------------------------------- /45_arch/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "arch" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | anyhow = "1" 10 | async-trait = "0.1" 11 | thiserror = "1" 12 | tokio = { version = "1", features = ["full"] } 13 | -------------------------------------------------------------------------------- /45_arch/src/lib.rs: -------------------------------------------------------------------------------- 1 | // see examples 2 | -------------------------------------------------------------------------------- /46_kv/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/405-studio/geektime-rust/3c57ef418118eacc24d2e35acb470aa4e6a6a672/46_kv/.gitignore -------------------------------------------------------------------------------- /46_kv/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let mut config = prost_build::Config::new(); 3 | config.bytes(&["."]); 4 | config.type_attribute(".", "#[derive(PartialOrd)]"); 5 | config 6 | .out_dir("src/pb") 7 | .compile_protos(&["abi.proto"], &["."]) 8 | .unwrap(); 9 | println!("cargo:rerun-if-changed=build.rs"); 10 | println!("cargo:rerun-if-changed=abi.proto"); 11 | } 12 | -------------------------------------------------------------------------------- /46_kv/examples/client.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use async_prost::AsyncProstStream; 3 | use futures::prelude::*; 4 | use kv6::{CommandRequest, CommandResponse}; 5 | use tokio::net::TcpStream; 6 | use tracing::info; 7 | 8 | #[tokio::main] 9 | async fn main() -> Result<()> { 10 | tracing_subscriber::fmt::init(); 11 | 12 | let addr = "127.0.0.1:9527"; 13 | // 连接服务器 14 | let stream = TcpStream::connect(addr).await?; 15 | 16 | // 使用 AsyncProstStream 来处理 TCP Frame 17 | let mut client = 18 | AsyncProstStream::<_, CommandResponse, CommandRequest, _>::from(stream).for_async(); 19 | 20 | // 生成一个 HSET 命令 21 | let cmd = CommandRequest::new_hset("table1", "hello", "world".to_string().into()); 22 | 23 | // 发送 HSET 命令 24 | client.send(cmd).await?; 25 | if let Some(Ok(data)) = client.next().await { 26 | info!("Got response {:?}", data); 27 | } 28 | 29 | Ok(()) 30 | } 31 | -------------------------------------------------------------------------------- /46_kv/fixtures/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/405-studio/geektime-rust/3c57ef418118eacc24d2e35acb470aa4e6a6a672/46_kv/fixtures/.gitkeep -------------------------------------------------------------------------------- /46_kv/fixtures/ca.cert: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIBeDCCASqgAwIBAgIJAK/ACf4lHrMIMAUGAytlcDAzMQswCQYDVQQGDAJDTjES 3 | MBAGA1UECgwJQWNtZSBJbmMuMRAwDgYDVQQDDAdBY21lIENBMB4XDTIxMDkyNjAx 4 | MjU1OVoXDTMxMDkyNDAxMjU1OVowMzELMAkGA1UEBgwCQ04xEjAQBgNVBAoMCUFj 5 | bWUgSW5jLjEQMA4GA1UEAwwHQWNtZSBDQTAqMAUGAytlcAMhAO6QG6Ma8p4xEZ0V 6 | 9VUutcHGutlezoR4E3geBYVojMSKo1swWTATBgNVHREEDDAKgghhY21lLmluYzAd 7 | BgNVHQ4EFgQU8kevGAonItSdc8VGWa74jwYUnHgwEgYDVR0TAQH/BAgwBgEB/wIB 8 | EDAPBgNVHQ8BAf8EBQMDBwYAMAUGAytlcANBAIzbgTAiy6SHCQxfJhlQAs1dIYgU 9 | jwyjxoeYKv/jgGoJd9fMBXsD94tEtyOwF6ph6JEJh0SwCLZrQw2OIp/H7QU= 10 | -----END CERTIFICATE----- 11 | -------------------------------------------------------------------------------- /46_kv/fixtures/ca.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MFMCAQEwBQYDK2VwBCIEIDGUGlTZG5ZZ0NC/qfb+A96ofGsg4pPchKss2VodfQwf 3 | oSMDIQDukBujGvKeMRGdFfVVLrXBxrrZXs6EeBN4HgWFaIzEig== 4 | -----END PRIVATE KEY----- 5 | -------------------------------------------------------------------------------- /46_kv/fixtures/client.cert: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIBXTCCAQ+gAwIBAgIJAKEchNUbV8e4MAUGAytlcDAzMQswCQYDVQQGDAJDTjES 3 | MBAGA1UECgwJQWNtZSBJbmMuMRAwDgYDVQQDDAdBY21lIENBMB4XDTIxMDkyNjAx 4 | MjU1OVoXDTIyMDkyNjAxMjU1OVowPTELMAkGA1UEBgwCQ04xEjAQBgNVBAoMCUFj 5 | bWUgSW5jLjEaMBgGA1UEAwwRYXdlc29tZS1kZXZpY2UtaWQwKjAFBgMrZXADIQDR 6 | 1n/nkKauN1RPwmK+gXe87WIXJw/QCHUOr4GEabT3qaM2MDQwEwYDVR0lBAwwCgYI 7 | KwYBBQUHAwIwDAYDVR0TBAUwAwEBADAPBgNVHQ8BAf8EBQMDB+AAMAUGAytlcANB 8 | APpIPMooTxwQA6aYP3C0ZeEouAq5FrbXihK0d3Wt+TTi8yUoH/miz5oHxYqGan+U 9 | 0EbIBKB+blgYwEPZ6u5Xcws= 10 | -----END CERTIFICATE----- 11 | -------------------------------------------------------------------------------- /46_kv/fixtures/client.conf: -------------------------------------------------------------------------------- 1 | [general] 2 | addr = '127.0.0.1:9527' 3 | 4 | [tls] 5 | domain = 'kvserver.acme.inc' 6 | ca = """ 7 | -----BEGIN CERTIFICATE-----\r 8 | MIIBeDCCASqgAwIBAgIJAK/ACf4lHrMIMAUGAytlcDAzMQswCQYDVQQGDAJDTjES\r 9 | MBAGA1UECgwJQWNtZSBJbmMuMRAwDgYDVQQDDAdBY21lIENBMB4XDTIxMDkyNjAx\r 10 | MjU1OVoXDTMxMDkyNDAxMjU1OVowMzELMAkGA1UEBgwCQ04xEjAQBgNVBAoMCUFj\r 11 | bWUgSW5jLjEQMA4GA1UEAwwHQWNtZSBDQTAqMAUGAytlcAMhAO6QG6Ma8p4xEZ0V\r 12 | 9VUutcHGutlezoR4E3geBYVojMSKo1swWTATBgNVHREEDDAKgghhY21lLmluYzAd\r 13 | BgNVHQ4EFgQU8kevGAonItSdc8VGWa74jwYUnHgwEgYDVR0TAQH/BAgwBgEB/wIB\r 14 | EDAPBgNVHQ8BAf8EBQMDBwYAMAUGAytlcANBAIzbgTAiy6SHCQxfJhlQAs1dIYgU\r 15 | jwyjxoeYKv/jgGoJd9fMBXsD94tEtyOwF6ph6JEJh0SwCLZrQw2OIp/H7QU=\r 16 | -----END CERTIFICATE-----\r 17 | """ 18 | -------------------------------------------------------------------------------- /46_kv/fixtures/client.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MFMCAQEwBQYDK2VwBCIEIHR35EEVaczWX7iBttg7LlOAYXzDsfRE1mOXJQjeryOR 3 | oSMDIQDR1n/nkKauN1RPwmK+gXe87WIXJw/QCHUOr4GEabT3qQ== 4 | -----END PRIVATE KEY----- 5 | -------------------------------------------------------------------------------- /46_kv/fixtures/server.cert: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIBdzCCASmgAwIBAgIICpy02U2yuPowBQYDK2VwMDMxCzAJBgNVBAYMAkNOMRIw 3 | EAYDVQQKDAlBY21lIEluYy4xEDAOBgNVBAMMB0FjbWUgQ0EwHhcNMjEwOTI2MDEy 4 | NTU5WhcNMjYwOTI1MDEyNTU5WjA6MQswCQYDVQQGDAJDTjESMBAGA1UECgwJQWNt 5 | ZSBJbmMuMRcwFQYDVQQDDA5BY21lIEtWIHNlcnZlcjAqMAUGAytlcAMhAK2Z2AjF 6 | A0uiltNuCvl6EVFl6tpaS/wJYB5IdWT2IISdo1QwUjAcBgNVHREEFTATghFrdnNl 7 | cnZlci5hY21lLmluYzATBgNVHSUEDDAKBggrBgEFBQcDATAMBgNVHRMEBTADAQEA 8 | MA8GA1UdDwEB/wQFAwMH4AAwBQYDK2VwA0EASGOmOWFPjbGhXNOmYNCa3lInbgRy 9 | iTNtB/5kElnbKkhKhRU7yQ8HTHWWkyU5WGWbOOIXEtYp+5ERUJC+mzP9Bw== 10 | -----END CERTIFICATE----- 11 | -------------------------------------------------------------------------------- /46_kv/fixtures/server.conf: -------------------------------------------------------------------------------- 1 | [general] 2 | addr = '127.0.0.1:9527' 3 | 4 | [storage] 5 | type = 'SledDb' 6 | args = '/tmp/kv_server' 7 | 8 | [tls] 9 | cert = """ 10 | -----BEGIN CERTIFICATE-----\r 11 | MIIBdzCCASmgAwIBAgIICpy02U2yuPowBQYDK2VwMDMxCzAJBgNVBAYMAkNOMRIw\r 12 | EAYDVQQKDAlBY21lIEluYy4xEDAOBgNVBAMMB0FjbWUgQ0EwHhcNMjEwOTI2MDEy\r 13 | NTU5WhcNMjYwOTI1MDEyNTU5WjA6MQswCQYDVQQGDAJDTjESMBAGA1UECgwJQWNt\r 14 | ZSBJbmMuMRcwFQYDVQQDDA5BY21lIEtWIHNlcnZlcjAqMAUGAytlcAMhAK2Z2AjF\r 15 | A0uiltNuCvl6EVFl6tpaS/wJYB5IdWT2IISdo1QwUjAcBgNVHREEFTATghFrdnNl\r 16 | cnZlci5hY21lLmluYzATBgNVHSUEDDAKBggrBgEFBQcDATAMBgNVHRMEBTADAQEA\r 17 | MA8GA1UdDwEB/wQFAwMH4AAwBQYDK2VwA0EASGOmOWFPjbGhXNOmYNCa3lInbgRy\r 18 | iTNtB/5kElnbKkhKhRU7yQ8HTHWWkyU5WGWbOOIXEtYp+5ERUJC+mzP9Bw==\r 19 | -----END CERTIFICATE-----\r 20 | """ 21 | key = """ 22 | -----BEGIN PRIVATE KEY-----\r 23 | MFMCAQEwBQYDK2VwBCIEIPMyINaewhXwuTPUufFO2mMt/MvQMHrGDGxgdgfy/kUu\r 24 | oSMDIQCtmdgIxQNLopbTbgr5ehFRZeraWkv8CWAeSHVk9iCEnQ==\r 25 | -----END PRIVATE KEY-----\r 26 | """ 27 | 28 | [log] 29 | path = '/tmp/kv-log' 30 | rotation = 'Daily' 31 | -------------------------------------------------------------------------------- /46_kv/fixtures/server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MFMCAQEwBQYDK2VwBCIEIPMyINaewhXwuTPUufFO2mMt/MvQMHrGDGxgdgfy/kUu 3 | oSMDIQCtmdgIxQNLopbTbgr5ehFRZeraWkv8CWAeSHVk9iCEnQ== 4 | -----END PRIVATE KEY----- 5 | -------------------------------------------------------------------------------- /47_48_macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "macros" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [lib] 9 | proc-macro = true 10 | 11 | [dependencies] 12 | anyhow = "1" 13 | askama = "0.11" # 处理 jinjia 模板,模板需要放在和 src 平行的 templates 目录下 14 | darling = "0.13" # 可以很方便的处理宏里面 attributes 15 | proc-macro2 = "1" # proc-macro 的封装 16 | quote = "1" # 用于生成代码的 TokenStream 17 | syn = { version = "1", features = ["extra-traits"] } # 用于解析 TokenStream,使用 extra-traits 可以用于 Debug 18 | -------------------------------------------------------------------------------- /47_48_macros/README.md: -------------------------------------------------------------------------------- 1 | # Rust 宏编程加餐代码 2 | 3 | 代码配合着 B 站视频食用,效果更加。 4 | 5 | 传送门: 6 | 7 | - [Rust 过程宏(1)](https://www.bilibili.com/video/BV1Za411q7LQ) 8 | - [Rust 过程宏(2)](https://www.bilibili.com/video/BV1Fu411m7W7) 9 | - [Rust 过程宏(3)](https://www.bilibili.com/video/BV1dr4y1v74n) 10 | -------------------------------------------------------------------------------- /47_48_macros/examples/command.rs: -------------------------------------------------------------------------------- 1 | use macros::Builder; 2 | 3 | #[allow(dead_code)] 4 | #[derive(Debug, Builder)] 5 | pub struct Command { 6 | executable: String, 7 | args: Vec, 8 | env: Vec, 9 | current_dir: Option, 10 | } 11 | 12 | fn main() { 13 | let command = Command::builder() 14 | .executable("cargo".to_owned()) 15 | .args(vec!["build".to_owned(), "--release".to_owned()]) 16 | .env(vec![]) 17 | .build() 18 | .unwrap(); 19 | assert!(command.current_dir.is_none()); 20 | 21 | let command = Command::builder() 22 | .executable("cargo".to_owned()) 23 | .args(vec!["build".to_owned(), "--release".to_owned()]) 24 | .env(vec![]) 25 | .current_dir("..".to_owned()) 26 | .build() 27 | .unwrap(); 28 | assert!(command.current_dir.is_some()); 29 | println!("{:?}", command); 30 | } 31 | -------------------------------------------------------------------------------- /47_48_macros/examples/command_with_attr.rs: -------------------------------------------------------------------------------- 1 | use macros::BuilderWithAttr; 2 | 3 | #[allow(dead_code)] 4 | #[derive(Debug, BuilderWithAttr)] 5 | pub struct Command { 6 | executable: String, 7 | #[builder(each = "arg")] 8 | args: Vec, 9 | #[builder(each = "env", default = "vec![]")] 10 | env: Vec, 11 | current_dir: Option, 12 | } 13 | 14 | fn main() { 15 | let command = Command::builder() 16 | .executable("cargo".to_owned()) 17 | .arg("build".to_owned()) 18 | .arg("--release".to_owned()) 19 | .build() 20 | .unwrap(); 21 | 22 | assert_eq!(command.executable, "cargo"); 23 | assert_eq!(command.args, vec!["build", "--release"]); 24 | println!("{:?}", command); 25 | } 26 | -------------------------------------------------------------------------------- /47_48_macros/examples/query.rs: -------------------------------------------------------------------------------- 1 | use macros::query; 2 | 3 | fn main() { 4 | query!(SELECT * FROM users WHERE age > 10); 5 | hello() 6 | } 7 | -------------------------------------------------------------------------------- /47_48_macros/examples/rule.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! my_vec { 3 | // 没带任何参数的 my_vec,我们创建一个空的 vec 4 | () => { 5 | std::vec::Vec::new() 6 | }; 7 | // 处理 my_vec![1, 2, 3, 4] 8 | ($($el:expr),*) => ({ 9 | let mut v = std::vec::Vec::new(); 10 | $(v.push($el);)* 11 | v 12 | }); 13 | // 处理 my_vec![0; 10] 14 | ($el:expr; $n:expr) => { 15 | std::vec::from_elem($el, $n) 16 | } 17 | } 18 | 19 | fn main() { 20 | let mut v = my_vec![]; 21 | v.push(1); 22 | // 调用时可以使用 [], (), {} 23 | let _v = my_vec!(1, 2, 3, 4); 24 | let _v = my_vec![1, 2, 3, 4]; 25 | let v = my_vec! {1, 2, 3, 4}; 26 | println!("{:?}", v); 27 | 28 | println!("{:?}", v); 29 | // 30 | let v = my_vec![1; 10]; 31 | println!("{:?}", v); 32 | } 33 | -------------------------------------------------------------------------------- /47_48_macros/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod builder; 2 | mod builder_with_attr; 3 | mod raw_builder; 4 | 5 | use proc_macro::TokenStream; 6 | use raw_builder::BuilderContext; 7 | use syn::{parse_macro_input, DeriveInput}; 8 | 9 | #[proc_macro] 10 | pub fn query(input: TokenStream) -> TokenStream { 11 | println!("{:#?}", input); 12 | "fn hello() { println!(\"Hello world!\"); }" 13 | .parse() 14 | .unwrap() 15 | } 16 | 17 | #[proc_macro_derive(RawBuilder)] 18 | pub fn derive_raw_builder(input: TokenStream) -> TokenStream { 19 | BuilderContext::render(input).unwrap().parse().unwrap() 20 | } 21 | 22 | #[proc_macro_derive(Builder)] 23 | pub fn derive_builder(input: TokenStream) -> TokenStream { 24 | let input = parse_macro_input!(input as DeriveInput); 25 | builder::BuilderContext::from(input).render().into() 26 | } 27 | 28 | #[proc_macro_derive(BuilderWithAttr, attributes(builder))] 29 | pub fn derive_builder_with_attr(input: TokenStream) -> TokenStream { 30 | let input = parse_macro_input!(input as DeriveInput); 31 | builder_with_attr::BuilderContext::from(input) 32 | .render() 33 | .into() 34 | } 35 | -------------------------------------------------------------------------------- /47_48_macros/templates/builder.j2: -------------------------------------------------------------------------------- 1 | impl {{ name }} { 2 | pub fn builder() -> {{ builder_name }} { 3 | Default::default() 4 | } 5 | } 6 | 7 | #[derive(Debug, Default)] 8 | pub struct {{ builder_name }} { 9 | {% for field in fields %} 10 | {{ field.name }}: Option<{{ field.ty }}>, 11 | {% endfor %} 12 | } 13 | 14 | impl {{ builder_name }} { 15 | {% for field in fields %} 16 | pub fn {{ field.name }}(mut self, v: impl Into<{{ field.ty }}>) -> {{ builder_name }} { 17 | self.{{ field.name }} = Some(v.into()); 18 | self 19 | } 20 | {% endfor %} 21 | 22 | pub fn build(self) -> Result<{{ name }}, &'static str> { 23 | Ok({{ name }} { 24 | {% for field in fields %} 25 | {% if field.optional %} 26 | {{ field.name }}: self.{{ field.name }}, 27 | {% else %} 28 | {{ field.name }}: self.{{ field.name }}.ok_or("Build failed: missing {{ field.name }}")?, 29 | {% endif %} 30 | {% endfor %} 31 | }) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | members = [ 4 | "02_concepts", 5 | "03_grammar/scrape_url", 6 | "03_grammar/scrape_url_args", 7 | "03_grammar/scrape_url_with_error_handling", 8 | "04_httpie", 9 | "05_thumbor", 10 | "06_queryer/queryer", 11 | "06_queryer/queryer-js", 12 | "06_queryer/queryer-py", 13 | "07_ownership", 14 | "08_borrow", 15 | "09_multi_owner", 16 | "10_lifetime", 17 | "11_memory", 18 | "12_type_system", 19 | "13_traits", 20 | "14_sys_traits", 21 | "15_smart_pointers", 22 | "16_data_structure", 23 | "17_hash_table", 24 | "19_closure", 25 | "21_kv", 26 | "mid_term_rgrep", 27 | "23_advanced_generics", 28 | "24_advanced_trait_objects", 29 | "26_kv", 30 | "29_network", 31 | "30_unsafe", 32 | "31_ffi/ffi", 33 | "31_ffi/ffi_math", 34 | "32_xunmi_py", 35 | "33_concurrency", 36 | "35_con_utils", 37 | "36_kv", 38 | "37_kv", 39 | "38_async", 40 | "39_async_internals", 41 | "40_async_stream", 42 | "41_kv", 43 | "42_kv", 44 | "43_docdoc", 45 | "44_data_processing", 46 | "45_arch", 47 | "46_kv", 48 | "ending", 49 | "47_48_macros", 50 | ] 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # geektime-rust 2 | 3 | 我的极客时间 [Rust 第一课](https://time.geekbang.org/column/intro/100085301) 的代码仓库,随课程更新 4 | 5 | ![rust 第一课](images/rust_qr.jpg) 6 | -------------------------------------------------------------------------------- /ending/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /ending/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ending" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | tracing = "0.1" 10 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 11 | reqwest = { version = "0.11", default-features = false, features = ["rustls-tls", "blocking"]} 12 | -------------------------------------------------------------------------------- /ending/examples/http2.rs: -------------------------------------------------------------------------------- 1 | use tracing::info; 2 | use tracing_subscriber::EnvFilter; 3 | 4 | fn main() { 5 | tracing_subscriber::fmt::fmt() 6 | .with_env_filter(EnvFilter::from_default_env()) 7 | .init(); 8 | 9 | let url = "https://www.rust-lang.org/"; 10 | 11 | let _body = reqwest::blocking::get(url).unwrap().text().unwrap(); 12 | info!("Fetching url: {}", url); 13 | } 14 | -------------------------------------------------------------------------------- /ending/src/lib.rs: -------------------------------------------------------------------------------- 1 | // see examples 2 | -------------------------------------------------------------------------------- /images/rust_qr.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/405-studio/geektime-rust/3c57ef418118eacc24d2e35acb470aa4e6a6a672/images/rust_qr.jpg -------------------------------------------------------------------------------- /mid_term_rgrep/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rgrep" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | anyhow = "1" 10 | clap = { version = "3", features = ["derive"] } 11 | colored = "2" 12 | glob = "0.3" 13 | itertools = "0.10" 14 | rayon = "1" 15 | regex = "1" 16 | thiserror = "1" 17 | -------------------------------------------------------------------------------- /mid_term_rgrep/src/error.rs: -------------------------------------------------------------------------------- 1 | use thiserror::Error; 2 | 3 | #[derive(Error, Debug)] 4 | pub enum GrepError { 5 | #[error("Glob pattern error")] 6 | GlobPatternError(#[from] glob::PatternError), 7 | #[error("Regex pattern error")] 8 | RegexPatternError(#[from] regex::Error), 9 | #[error("I/O error")] 10 | IoError(#[from] std::io::Error), 11 | } 12 | -------------------------------------------------------------------------------- /mid_term_rgrep/src/main.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use clap::Parser; 3 | use rgrep::*; 4 | 5 | fn main() -> Result<()> { 6 | let config: GrepConfig = GrepConfig::parse(); 7 | config.match_with_default_strategy()?; 8 | 9 | Ok(()) 10 | } 11 | --------------------------------------------------------------------------------