diff --git a/.gitignore b/.gitignore index 400485b..686b676 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ **/target/ .DS_Store +*.rlib diff --git a/.vscode/settings.json b/.vscode/settings.json index 2535759..421d036 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,5 @@ { "rust-analyzer.linkedProjects": [ - "./9.Functions/Cargo.toml" + "./14.Generics/14.9 Phantom type parameters/Cargo.toml" ] } diff --git a/10.Modules/10.1 Visibility/Cargo.lock b/10.Modules/10.1 Visibility/Cargo.lock new file mode 100644 index 0000000..f3e3f38 --- /dev/null +++ b/10.Modules/10.1 Visibility/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "visibility" +version = "0.1.0" diff --git a/10.Modules/10.1 Visibility/Cargo.toml b/10.Modules/10.1 Visibility/Cargo.toml new file mode 100644 index 0000000..98027af --- /dev/null +++ b/10.Modules/10.1 Visibility/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "visibility" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/10.Modules/10.1 Visibility/src/main.rs b/10.Modules/10.1 Visibility/src/main.rs new file mode 100644 index 0000000..b721c78 --- /dev/null +++ b/10.Modules/10.1 Visibility/src/main.rs @@ -0,0 +1,127 @@ +//! +//! # 模块化 +//! +//! Rust 提供了非常强大的模块化系统,可以按照等级拆分代码到小的模块中。 +//! 模块中还可以通过 `pub` 关键字控制代码是否对外可见。 +//! +//! 模块中可以放置 函数(function)、结构体(struct)、特性(trait)、和实现(impl)、甚至其他的模块(module) +//! + +/// 默认情况下模块内部的所有内容都是私有(private)的, +/// 需要通过 `pub` 关键字来让私有变成外部可访问的。 +// 模块名为 my_mod 的模块 +mod my_mod { + // 模块内部的私有函数,模块外部不可见,也不能使用。 + fn private_function() { + println!("called `my_mod::private_function()`"); + } + + // 使用 `pub` 关键字让 `function` 函数外面可以调用。 + pub fn function() { + println!("called `my_mod::function()`"); + } + + // 公开的函数内部可以使用当前模块内的任意方法,不管使用的函数是不是公开的。 + pub fn indirect_access() { + print!("called `my_mod::indirect_access()`, that\n> "); + private_function(); + } + + // 模块可以嵌套使用。 + pub mod nested { + pub fn function() { + println!("called `my_mod::nested::function()`"); + } + + #[allow(dead_code)] + fn private_function() { + println!("called `my_mod::nested::private_function()`"); + } + + // 可以通过 `pub(in path)` 的语法来指定某些方法,只允许指定的父模块访问, + // `path` 只能是父模块或者祖先模块。 + pub(in crate::my_mod) fn public_function_in_my_mod() { + print!("called `my_mod::nested::public_function_in_my_mod()`, that\n> "); + public_function_in_nested(); + } + + // 如果使用 `pub(self)` 关键字定义的话,那就表示该函数只能在当前模块内部使用, + // 等同于 private + pub(self) fn public_function_in_nested() { + println!("called `my_mod::nested::public_function_in_nested()`"); + } + + // `pub(super)` 等同于当前的父模块,`super` 关键字就是父模块的别名 + pub(super) fn public_function_in_super_mod() { + println!("called `my_mod::nested::public_function_in_super_mod()`"); + } + } + + pub fn call_public_function_in_my_mod() { + print!("called `my_mod::call_public_function_in_my_mod()`, that\n> "); + nested::public_function_in_my_mod(); + print!("> "); + nested::public_function_in_super_mod(); + } + + // pub(crate) 表示这个函数只允许在当前的包内可用,`crate` 等于当前的包 + pub(crate) fn public_function_in_crate() { + println!("called `my_mod::public_function_in_crate()`"); + } + + // 嵌套的模块依旧遵循私有规则 + mod private_nested { + #[allow(dead_code)] + pub fn function() { + println!("called `my_mod::private_nested::function()`"); + } + + // 因为当前模块没有对外公开,所以即使私有模块内部的方法公开了访问作用域,依旧还是不可见的。 + #[allow(dead_code)] + pub(crate) fn restricted_function() { + println!("called `my_mod::private_nested::restricted_function()`"); + } + } +} + +fn function() { + println!("called `function()`"); +} + +fn main() { + // 因为模块有独立的作用域,所以可以避免同名函数的冲突问题。 + function(); // 全局作用域的函数 + + my_mod::function(); // 模块内部的函数 + + // 在模块外面可以通过模块的名称,使用 `::` 作用域访问关键字访问模块内部的方法,或者嵌套在模块内部的模块。 + my_mod::indirect_access(); + my_mod::nested::function(); + my_mod::call_public_function_in_my_mod(); + + // 使用 `pub(crate)` 声明的方法,只要在同一个包(crate)中就可以随意使用。 + my_mod::public_function_in_crate(); + + // 在模块中通过 `pub(in path)` 声明的方法,只能在 `path` 限定的地方使用 + // 报错!函数 `public_function_in_my_mod` 是私有的 + // my_mod::nested::public_function_in_my_mod(); + // TODO ^ 移除注释查看错误 + + // 模块的私有项在外部不管什么情况下都不能被直接访问 + + // 报错! `private_function` 是私有的 + //my_mod::private_function(); + // TODO ^ 移除注释查看错误 + + // 报错! `private_function` 是私有的 + //my_mod::nested::private_function(); + // TODO ^ 移除注释查看错误 + + // 报错! `private_nested` 是私有模块 + //my_mod::private_nested::function(); + // TODO ^ 移除注释查看错误 + + // 报错! `private_nested` 是私有模块 + //my_mod::private_nested::restricted_function(); + // TODO ^ 移除注释查看错误 +} diff --git a/10.Modules/10.2 Struct visibility/Cargo.lock b/10.Modules/10.2 Struct visibility/Cargo.lock new file mode 100644 index 0000000..3766631 --- /dev/null +++ b/10.Modules/10.2 Struct visibility/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "struct_visibility" +version = "0.1.0" diff --git a/10.Modules/10.2 Struct visibility/Cargo.toml b/10.Modules/10.2 Struct visibility/Cargo.toml new file mode 100644 index 0000000..27f2a58 --- /dev/null +++ b/10.Modules/10.2 Struct visibility/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "struct_visibility" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/10.Modules/10.2 Struct visibility/src/main.rs b/10.Modules/10.2 Struct visibility/src/main.rs new file mode 100644 index 0000000..3af7408 --- /dev/null +++ b/10.Modules/10.2 Struct visibility/src/main.rs @@ -0,0 +1,55 @@ +//! +//! 模块中结构体的可见性 +//! +//! 结构体的字段拥有一个额外的可见性配置, +//! 默认情况下结构体内部的字段对外都是不可见的, +//! 需要在每个字段上手动指定 `pub` 来让外部可以访问指定的字段, +//! +//! + +mod my { + // 这个结构体对外可见 + pub struct OpenBox { + // 这个字段也对外可见 + pub contents: T, + } + + // 这个结构体对外可见 + pub struct ClosedBox { + // 这个字段外面不可见 + #[allow(dead_code)] + contents: T, + } + + impl ClosedBox { + // 结构体上拥有一个 `new` 方法是对外可见的 + pub fn new(contents: T) -> ClosedBox { + ClosedBox { contents } + } + } +} + +fn main() { + // 如果结构体可见并且所有的字段也都对外可见的话,则可以直接声明字面量就能的到结构体实例 + let open_box = my::OpenBox { + contents: "public information", + }; + + // 结构体的字段可以直接访问 + println!("The open box contains: {}", open_box.contents); + + // 公开的结构体如果有私有的字段,则不能直接使用字面量形式构造实例,因为私有字段对外不可见。 + // 错误!`ClosedBox` 拥有不可见的私有字段。 + // let closed_box = my::ClosedBox { + // contents: "classified information", + // }; + // TODO ^ 移除注释查看错误 + + // 拥有私有结构的结构体可以通过结构上的公开的关联函数来构造结构体的实例。 + let _closed_box = my::ClosedBox::new("classified information"); + + // 结构体的私有字段外部是不能访问的。 + // 错误! `contents` 字段是私有的 + // println!("The closed box contains: {}", _closed_box.contents); + // TODO ^ 移除注释查看错误 +} diff --git a/10.Modules/10.3 The use declaration/Cargo.lock b/10.Modules/10.3 The use declaration/Cargo.lock new file mode 100644 index 0000000..e05a7ca --- /dev/null +++ b/10.Modules/10.3 The use declaration/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "the_use_declaration" +version = "0.1.0" diff --git a/10.Modules/10.3 The use declaration/Cargo.toml b/10.Modules/10.3 The use declaration/Cargo.toml new file mode 100644 index 0000000..efb5fd4 --- /dev/null +++ b/10.Modules/10.3 The use declaration/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "the_use_declaration" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/10.Modules/10.3 The use declaration/src/main.rs b/10.Modules/10.3 The use declaration/src/main.rs new file mode 100644 index 0000000..686c17a --- /dev/null +++ b/10.Modules/10.3 The use declaration/src/main.rs @@ -0,0 +1,69 @@ +//! +//! 使用 `use` 声明 +//! +//! `use` 关键字可以把一个完整的路径在当前的作用域中, +//! 重新绑定到一个新的名字,这样可以减少完整路径的使用。 +//! +//! 常用的几种形式如下 +//! +//! ``` +//! { +//! // 把 `std::rc::Rc` 使用 `Rc` 这个名字绑定到当前的作用域下 +//! use std::rc::Rc; +//! } +//! +//! { +//! // 把 `std::rc::Rc` 使用 `StdRc` 这个名字绑定到当前的作用域下 +//! use std::rc::Rc as StdRc; +//! } +//! +//! { +//! // 把 `std::rc` 下的所有导出项全都绑定到当前作用域下 +//! use std::rc::*; +//! } +//! +//! { +//! // `self` 关键字代表的是前一级的路径, +//! // 在这里 `self` 等于把 `std::rc` 使用 `rc` 这个关键字绑定到当前作用域, +//! // 同时把 `std::rc::Rc` 使用 `StdRc` 绑定到当前作用域, +//! // 和 `std::rc::Weak` 使用 `Weak` 绑定到当前作用域。 +//! use std::rc::{self, Rc as StdRc, Weak}; +//! } +//! +//! ``` +//! + +// 把 `deeply::nested::function` 绑定到 `other_function`。 +use deeply::nested::function as other_function; + +fn function() { + println!("called `function()`"); +} + +mod deeply { + pub mod nested { + pub fn function() { + println!("called `deeply::nested::function()`"); + } + } +} + +fn main() { + // 使用重新绑定的名字调用 `deeply::nested::function` + other_function(); + + println!("Entering block"); + { + // `crate` 代表当前的包,这里相当于把 `crate::deeply::nested::function` 重新绑定到当前作用域,名字叫 `function`, + // 因为 `shadowing(遮蔽)` 的规则,`use` 关键字重新绑定的 `function` 会覆盖掉当前的全局作用域下的 `function` 函数。 + use crate::deeply::nested::function; + + // 这里调用的函数实际上是 `deeply::nested::function`, + // 因为 `use` 关键字的重新绑定在当前的嵌套作用域中遮蔽了全局作用域下的 `function` + function(); + + println!("Leaving block"); + } + + function(); +} diff --git a/10.Modules/10.4 super and self/Cargo.lock b/10.Modules/10.4 super and self/Cargo.lock new file mode 100644 index 0000000..52e6337 --- /dev/null +++ b/10.Modules/10.4 super and self/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "super_and_self" +version = "0.1.0" diff --git a/10.Modules/10.4 super and self/Cargo.toml b/10.Modules/10.4 super and self/Cargo.toml new file mode 100644 index 0000000..b5a9e08 --- /dev/null +++ b/10.Modules/10.4 super and self/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "super_and_self" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/10.Modules/10.4 super and self/src/main.rs b/10.Modules/10.4 super and self/src/main.rs new file mode 100644 index 0000000..64b508b --- /dev/null +++ b/10.Modules/10.4 super and self/src/main.rs @@ -0,0 +1,55 @@ +//! +//! `super` 和 `self` 关键字 +//! +//! 这两个关键字可以让硬编码的模块路径写成相对的路径关系。 +//! +fn function() { + println!("called `function()`"); +} + +mod cool { + pub fn function() { + println!("called `cool::function()`"); + } +} + +mod my { + fn function() { + println!("called `my::function()`"); + } + + mod cool { + pub fn function() { + println!("called `my::cool::function()`"); + } + } + + pub fn indirect_call() { + // 在当前作用域中尝试访问所有的 `function` 函数! + print!("called `my::indirect_call()`, that\n> "); + + // `self` 关键字明确的指出,我们要访问的作用域是当前的模块。 + // 所以 `self::function()` 指明了我们调用的函数是当前模块中定义的函数。 + self::function(); + + // 如果没有指明作用域,那么作用域等同于 `self`。 + function(); + + // 可以使用 `self` 关键字来明确的指出,我们访问的是当前模块下定义的 `my` 子模块。 + // `self` 关键字是可以省略的,因为当不指明作用域的时候等同于 `self`。 + self::cool::function(); + + // `super` 关键字明确的指明了我们需要访问的是父模块中的方法,也就是当前的 `my` 模块的上一级。 + super::function(); + + // 还可以通过 `crate` 特殊的作用域来重新从当前的包的根作用域下重新指定一个完整的路径。 + { + use crate::cool::function as root_function; + root_function(); + } + } +} + +fn main() { + my::indirect_call(); +} diff --git a/10.Modules/10.5 File hierarchy/Cargo.lock b/10.Modules/10.5 File hierarchy/Cargo.lock new file mode 100644 index 0000000..986093e --- /dev/null +++ b/10.Modules/10.5 File hierarchy/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "file_hierarchy" +version = "0.1.0" diff --git a/10.Modules/10.5 File hierarchy/Cargo.toml b/10.Modules/10.5 File hierarchy/Cargo.toml new file mode 100644 index 0000000..a335d96 --- /dev/null +++ b/10.Modules/10.5 File hierarchy/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "file_hierarchy" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/10.Modules/10.5 File hierarchy/src/main.rs b/10.Modules/10.5 File hierarchy/src/main.rs new file mode 100644 index 0000000..321058d --- /dev/null +++ b/10.Modules/10.5 File hierarchy/src/main.rs @@ -0,0 +1,28 @@ +//! +//! 通过文件结构组织模块 +//! +//! Rust 的模块可以按照文件的结构来组织, +//! 目录会当做一个子模块的查找路径。 +//! + +// 这个定义会让 `Rust` 去尝试查找 `my.rs` 或者 `my/mod.rs` 这个文件,并把该文件当做一个模块。 +// `my` 模块的嵌套模块可以创建一个 `my` 的目录,把子模块放进去就可以了。 +mod my; + +fn function() { + println!("called `function()`"); +} + +fn main() { + // 调用 `my` 模块中的方法。 + my::function(); + + // 调用全局作用域下的方法。 + function(); + + // 再次调用 `my` 模块中的方法。 + my::indirect_access(); + + // 调用 `my` 模块中嵌套的子模块的方法。 + my::nested::function(); +} diff --git a/10.Modules/10.5 File hierarchy/src/my.rs b/10.Modules/10.5 File hierarchy/src/my.rs new file mode 100644 index 0000000..aaf7bd0 --- /dev/null +++ b/10.Modules/10.5 File hierarchy/src/my.rs @@ -0,0 +1,21 @@ +// 把 `inaccessible.rs` 或 `inaccessible/mod.rs` 当做一个子模块引用进来,但是该模块对外不可见。 +mod inaccessible; +// 把 `nested.rs` 或 `nested/mod.rs` 当做一个子模块引用进来,并且对外公开该模块。 +pub mod nested; + +// 定义公开方法 +pub fn function() { + println!("called `my::function()`"); +} + +// 私有方法 +fn private_function() { + println!("called `my::private_function()`"); +} + +// 公开方法中引用私有方法。 +pub fn indirect_access() { + print!("called `my::indirect_access()`, that\n> "); + + private_function(); +} diff --git a/10.Modules/10.5 File hierarchy/src/my/inaccessible.rs b/10.Modules/10.5 File hierarchy/src/my/inaccessible.rs new file mode 100644 index 0000000..08e7722 --- /dev/null +++ b/10.Modules/10.5 File hierarchy/src/my/inaccessible.rs @@ -0,0 +1,5 @@ +// 定义公开方法 +#[allow(dead_code)] +pub fn public_function() { + println!("called `my::inaccessible::public_function()`"); +} diff --git a/10.Modules/10.5 File hierarchy/src/my/nested.rs b/10.Modules/10.5 File hierarchy/src/my/nested.rs new file mode 100644 index 0000000..7125347 --- /dev/null +++ b/10.Modules/10.5 File hierarchy/src/my/nested.rs @@ -0,0 +1,10 @@ +// 公开发方法 +pub fn function() { + println!("called `my::nested::function()`"); +} + +// 私有方法 +#[allow(dead_code)] // 这个是为了禁用未使用的警告,因为该函数是私有的,但是又没有任何地方访问它。 +fn private_function() { + println!("called `my::nested::private_function()`"); +} diff --git a/11.Crates/rary.rs b/11.Crates/rary.rs new file mode 100644 index 0000000..38cafdb --- /dev/null +++ b/11.Crates/rary.rs @@ -0,0 +1,31 @@ +//! +//! `crate(包)` 是一个 Rust 的编译单元,不管什么时候使用 `rustc some_file.rs`, +//! 都会把输入的文件 `some_files.rs` 当做一个 `crate(包)`, +//! 如果输入的文件中有 `mod` 关键字,则会尝试把对应的声明文件的内容插入到 `mod` 关键字中, +//! 当所有的 `mod` 关键字都处理完了以后,编译器才开始进行编译, +//! +//! 也就是说单独的模块是不能进行编译的,只有 `crate(包)` 才能进行编译。 +//! +//! 一个 `crate(包)` 可以编译成可执行的二进制文件也可以编译成一个库(library)文件, +//! 默认情况下 `rustc` 会把 `crate(包)` 编译成可执行的二进制文件,可以通过额外的 +//! 命令行参数 `--crate-type` 参数指定为 `lib` 来修改这个行为。 +//! + +// 创建一个简单的库(library) +// 然后使用 `rustc --crate-type lib rary.rs` 编译成一个库, +// 编译完成后会自动生成一个 `library.rlib` 的文件,该文件就是生成好的库文件, +// 编译输出的文件名可以通过 `--crate-name` 来指定。 + +pub fn public_function() { + println!("called rary's `public_function()`"); +} + +fn private_function() { + println!("called rary's `private_function()`"); +} + +pub fn indirect_access() { + print!("called rary's `indirect_access()`, that\n> "); + + private_function(); +} diff --git a/11.Crates/using_library.rs b/11.Crates/using_library.rs new file mode 100644 index 0000000..8b8b0fd --- /dev/null +++ b/11.Crates/using_library.rs @@ -0,0 +1,20 @@ +//! +//! 使用库文件 +//! +//! 想要让包连接到一个库,需要使用 rustc 的 --extern 的参数来指定库名称, +//! 比如 `rustc using_library.rs --extern rary=library.rlib`, +//! 通过参数指定的库会绑定通过指定的名字当做一个模块导入到当前做的作用域中。 +//! 编译以后会生成一个 `using_library` 的二进制可执行文件。 +//! + +// Rust 2015版本之前需要明确的导入需要使用的外部库 +// extern crate rary; + +fn main() { + rary::public_function(); + + // 错误!`private_function` 是私有函数。 + //rary::private_function(); + + rary::indirect_access(); +} diff --git a/12.Cargo/12.1 Dependencies/Cargo.lock b/12.Cargo/12.1 Dependencies/Cargo.lock new file mode 100644 index 0000000..befd478 --- /dev/null +++ b/12.Cargo/12.1 Dependencies/Cargo.lock @@ -0,0 +1,169 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "anstream" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" + +[[package]] +name = "anstyle-parse" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "clap" +version = "4.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfaff671f6b22ca62406885ece523383b9b64022e341e53e009a62ebc47a45f2" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a216b506622bb1d316cd51328dce24e07bdff4a6128a47c7e7fad11878d5adbb" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_lex" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "dependencies" +version = "0.1.0" +dependencies = [ + "clap", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" diff --git a/12.Cargo/12.1 Dependencies/Cargo.toml b/12.Cargo/12.1 Dependencies/Cargo.toml new file mode 100644 index 0000000..e747bb0 --- /dev/null +++ b/12.Cargo/12.1 Dependencies/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "dependencies" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +clap = "4.4.11" diff --git a/12.Cargo/12.1 Dependencies/src/main.rs b/12.Cargo/12.1 Dependencies/src/main.rs new file mode 100644 index 0000000..4ec89cc --- /dev/null +++ b/12.Cargo/12.1 Dependencies/src/main.rs @@ -0,0 +1,70 @@ +//! +//! `cargo` 是官方提供的一个包管理工具, +//! 包含了非常多特性的可以提升代码的质量和开发效率 +//! +//! 包含的特性如下 +//! - 通过 `crates.io` (官方维护的包注册库)来管理管理包的集成和依赖管理 +//! - 内置单元测试 +//! - 内置基准测试 +//! + +//! +//! 大多数的程序都会使用外部的库,如果你曾经手动管理过依赖的话,肯定会知道手动管理依赖是非常困难的事情。 +//! `Rust` 的生态系统包含了很多标准的库,可以通过 `cargo` 来管理工程。 +//! +//! 通过 `cargo` 创建工程 +//! `cargo new foo` 创建一个可行文件的工程 +//! +//! 通过 `cargo` 创建库 +//! `cargo new --lib bar` 创建一个库 +//! +//! 创建完以后会自动生成一个目录,目录结构如下 +//! +//! ```sh +//! . +//! ├── bar +//! │ ├── Cargo.toml // 当前工程的配置文件 +//! │ └── src +//! │ └── lib.rs // 库的入口文件 +//! └── foo +//! ├── Cargo.toml +//! └── src +//! └── main.rs // 可执行文件的入口文件 +//! +//! ``` +//! +//! `Cargo.toml` 文件内容如下 +//! ```toml +//! [package] +//! name = "foo" +//! version = "0.1.0" +//! authors = ["mark"] + +//! [dependencies] +//! ``` +//! +//! `[package]` 下面的 `name` 表示当前工程的名字,这个名字就是发布到 `crates.io` 上面的名字, +//! 并且该名字还是输出的二进制文件名。 +//! `version` 是一个语义化的版本号。 +//! `authors` 是当前的用户名,也是将来发布到 `crates.io` 上面的以后使用的名字。 +//! +//! `[dependencies]` 就是当前工程所需要的依赖项了,这些依赖项都会自动从 `crates.io` 上面下载。 +//! 你可以在 `creates.io` 上面找到你所需要的依赖包,这里使用 `clap` 举例子,这个包现在最新的版本是 `4.4.11` +//! 我们可以在 `[dependencies]` 下添加 `clap = "4.4.11"`,这样我们就可以在工程中使用 `clap` 了。 +//! +//! `[dependencies]` 还支持更多语法,常用的有下面几种 +//! +//! ```toml +//! clap = "2.27.1" # 从 `crates.io` 上面下载 +//! rand = { git = "https://github.com/rust-lang-nursery/rand" } # 从指定的 `git` 仓库中下载 +//! bar = { path = "../bar" } # 使用本地文件目录 +//! ``` +//! +//! 我们可以在工程的任意目录中使用 `cargo build` 来构建工程,或者使用 `cargo run` 来直接运行工程。 +//! 这两个命令都会自动的去下载工程中所需要的依赖项。 +//! +//! + +fn main() { + println!("Hello, world!"); +} diff --git a/12.Cargo/12.2 Conventions/Cargo.lock b/12.Cargo/12.2 Conventions/Cargo.lock new file mode 100644 index 0000000..9fb6be0 --- /dev/null +++ b/12.Cargo/12.2 Conventions/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "conventions" +version = "0.1.0" diff --git a/12.Cargo/12.2 Conventions/Cargo.toml b/12.Cargo/12.2 Conventions/Cargo.toml new file mode 100644 index 0000000..2a5fc6c --- /dev/null +++ b/12.Cargo/12.2 Conventions/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "conventions" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/12.Cargo/12.2 Conventions/src/bin/my_other_bin.rs b/12.Cargo/12.2 Conventions/src/bin/my_other_bin.rs new file mode 100644 index 0000000..17a6c70 --- /dev/null +++ b/12.Cargo/12.2 Conventions/src/bin/my_other_bin.rs @@ -0,0 +1,4 @@ +// 另外一个二进制文件 +fn main() { + println!("other bin file"); +} diff --git a/12.Cargo/12.2 Conventions/src/main.rs b/12.Cargo/12.2 Conventions/src/main.rs new file mode 100644 index 0000000..f5da5bc --- /dev/null +++ b/12.Cargo/12.2 Conventions/src/main.rs @@ -0,0 +1,11 @@ +//! 默认约定 +//! +//! 默认情况下 `cargo` 编译工程只会把 `src/main.rs` 编译成为一个可执行文件, +//! 如果一个工程输出多个可执行文件应该怎么做呢? +//! +//! `cargo` 会把 `src/bin/` 目录下的所有文件都会额外的多生成一个可执行文件。 +//! + +fn main() { + println!("Hello, world!"); +} diff --git a/12.Cargo/12.3 Testing/Cargo.lock b/12.Cargo/12.3 Testing/Cargo.lock new file mode 100644 index 0000000..aa37fc3 --- /dev/null +++ b/12.Cargo/12.3 Testing/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "testing" +version = "0.1.0" diff --git a/12.Cargo/12.3 Testing/Cargo.toml b/12.Cargo/12.3 Testing/Cargo.toml new file mode 100644 index 0000000..2b5df64 --- /dev/null +++ b/12.Cargo/12.3 Testing/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "testing" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/12.Cargo/12.3 Testing/src/main.rs b/12.Cargo/12.3 Testing/src/main.rs new file mode 100644 index 0000000..af68d3d --- /dev/null +++ b/12.Cargo/12.3 Testing/src/main.rs @@ -0,0 +1,98 @@ +//! +//! 集成测试和单元测试 +//! +//! 对于任何软件都一样,集成测试和单元测试都是非常重要的一环,可以用来保证软件的质量。 +//! `Rust` 中对于单元测试和集成测试是第一优先级支持的事情。 +//! +//! 对于单元测试一般都会直接放在模块中,而集成测试则会放在根目录下的 `tests` 目录中。 +//! +//! 文件结构如下 +//! +//! ```sh +//! foo +//! ├── Cargo.toml +//! ├── src +//! │ └── main.rs +//! │ └── lib.rs +//! └── tests +//! ├── my_test.rs +//! └── my_other_test.rs +//! ``` +//! +//! 可以使用 `cargo tests` 来执行整个工程的测试,该命令会先执行`单元测试`,通过了以后会执行 `集成测试`。 +//! 还可以使用额外的参数来指定执行某些测试 `cargo test test_foo`,该命令指只会执行匹配 `test_foo` 的测试代码。 +//! +//! `cargo` 运行测试的时候是并行的,所以需要注意一点的是测试的代码不能出现竞争关系。 +//! +fn main() { + println!("Hello, world!"); +} + +/// 可能会出现竞争关系的测试代码。 +/// 这个测试运行完成以后,文件的内容预期是 +/// ```text +/// Ferris +/// Ferris +/// Ferris +/// Ferris +/// Ferris +/// Corro +/// Corro +/// Corro +/// Corro +/// Corro +/// ``` +/// +/// 但是实际上可能是这样的 +/// ```text +/// Corro +/// Ferris +/// Corro +/// Ferris +/// Corro +/// Ferris +/// Corro +/// Ferris +/// Corro +/// Ferris +/// ``` +#[cfg(test)] +mod tests { + // 导入使用的模块 + use std::fs::OpenOptions; + use std::io::Write; + + // 写文件 + #[test] + fn test_file() { + // 打开 `ferris.txt` 文件,如果文件不存在则创建文件。 + let mut file = OpenOptions::new() + .append(true) + .create(true) + .open("ferris.txt") + .expect("Failed to open ferris.txt"); + + // 向文件中写入5次 `Ferris` + for _ in 0..5 { + file.write_all("Ferris\n".as_bytes()) + .expect("Could not write to ferris.txt"); + } + } + + // 写上一个测试同一个文件 + #[test] + fn test_file_also() { + // 同上 + let mut file = OpenOptions::new() + .append(true) + .create(true) + .open("ferris.txt") + .expect("Failed to open ferris.txt"); + + // 向文件中写入5次 `Corro` + for _ in 0..5 { + file.write_all("Corro\n".as_bytes()) + .expect("Could not write to ferris.txt"); + } + } +} diff --git a/12.Cargo/12.4 Build Scripts/Cargo.lock b/12.Cargo/12.4 Build Scripts/Cargo.lock new file mode 100644 index 0000000..281a3ac --- /dev/null +++ b/12.Cargo/12.4 Build Scripts/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "build_scripts" +version = "0.1.0" diff --git a/12.Cargo/12.4 Build Scripts/Cargo.toml b/12.Cargo/12.4 Build Scripts/Cargo.toml new file mode 100644 index 0000000..4e2589c --- /dev/null +++ b/12.Cargo/12.4 Build Scripts/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "build_scripts" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/12.Cargo/12.4 Build Scripts/src/main.rs b/12.Cargo/12.4 Build Scripts/src/main.rs new file mode 100644 index 0000000..c5d07ae --- /dev/null +++ b/12.Cargo/12.4 Build Scripts/src/main.rs @@ -0,0 +1,23 @@ +//! +//! 构建脚本 +//! 有些时候仅仅使用 `cargo build` 是不够的,有可能你依赖了一些其他的东西, +//! 库或者需要动态生成一些代码,或者需要预编译一些其他的代码,才能让工程能正常编译。 +//! +//! 所以 `cargo` 提供了一个构建脚本的能力,可以通过构建脚本来让 `cargo` 在开始编译 +//! 工程以前先执行某些特定的操作。 +//! +//! 你可以在 `Cargo.toml` 文件的 `[package]` 中添加一个 `build = "build.rs"` +//! 属性来指定执行 `build.rs`,来做一些其他的操作,如果没有指定这个字段的话, +//! `cargo` 会默认使用项目根目录下的 `build.rs` 文件。 +//! +//! 构建脚本和其他的 `Rust` 代码一样,只不过 `cargo` 会在工程编译之前先执行构建脚本。 +//! +//! 你可以在构建脚本中使用一些 `cargo` 预定义好的环境变量,这些变量可以在[这里](https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-build-scripts)找到 +//! 构建脚本在执行过程中输出的内容可以在 `target/debug/build//output` 中找到。 +//! 输出的内容中使用 `cargo:` 开头的内容会被 `cargo` 直接解析,通过这些命令可以定义 `cargo` 的一些行为。 +//! +//! 完整的指令列表可以在[这里](https://doc.rust-lang.org/cargo/reference/build-scripts.html)找到 +//! +fn main() { + println!("Hello, world!"); +} diff --git a/13.Attributes/13 Attributes/Cargo.lock b/13.Attributes/13 Attributes/Cargo.lock new file mode 100644 index 0000000..689e00e --- /dev/null +++ b/13.Attributes/13 Attributes/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "attributes" +version = "0.1.0" diff --git a/13.Attributes/13 Attributes/Cargo.toml b/13.Attributes/13 Attributes/Cargo.toml new file mode 100644 index 0000000..9ed643a --- /dev/null +++ b/13.Attributes/13 Attributes/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "attributes" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/13.Attributes/13 Attributes/src/main.rs b/13.Attributes/13 Attributes/src/main.rs new file mode 100644 index 0000000..178c43c --- /dev/null +++ b/13.Attributes/13 Attributes/src/main.rs @@ -0,0 +1,30 @@ +//! +//! 属性是一个元数据,可以使用在 模块、包、等等,这些元数据可以实现以下的功能 +//! +//! - 条件编译 +//! - 设置包名称,版本和类型 +//! - 禁止语法检查和警告 +//! - 启用编译器的某些特性 +//! - 引用外部的库 +//! - 标记函数是单元测试 +//! - 标记函数是基准测试的一部分 +//! - 类似宏的属性 +//! +//! 如果一个属性的生效范围是整个包(crate)的时候语法是 `#![crate_attribute]`, +//! 如果只是指定的某个函数或模块等等的时候语法是 `#[item_attribute]`,没有 `!` 符号。 +//! +//! 属性还可以接收参数,下面的几种语法都代表属性接收参数。 +//! +//! - `#[attribute = "value"]` +//! - `#[attribute(key = "value")]` +//! - `#[attribute(value)]` +//! +//! 属性可以接收多个参数,如下所示 +//! +//! - `#[attribute(value, value2)]` +//! - `#[attribute(value, value2, value3 +//! value4, value5)]` +//! +fn main() { + println!("Hello, world!"); +} diff --git a/13.Attributes/13.1 dead_code/Cargo.lock b/13.Attributes/13.1 dead_code/Cargo.lock new file mode 100644 index 0000000..6d46dd0 --- /dev/null +++ b/13.Attributes/13.1 dead_code/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "dead_code" +version = "0.1.0" diff --git a/13.Attributes/13.1 dead_code/Cargo.toml b/13.Attributes/13.1 dead_code/Cargo.toml new file mode 100644 index 0000000..ed32b36 --- /dev/null +++ b/13.Attributes/13.1 dead_code/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "dead_code" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/13.Attributes/13.1 dead_code/src/main.rs b/13.Attributes/13.1 dead_code/src/main.rs new file mode 100644 index 0000000..ebd9757 --- /dev/null +++ b/13.Attributes/13.1 dead_code/src/main.rs @@ -0,0 +1,19 @@ +//! 死代码警告 +//! 编译器会在编译的过程中对没有使用的代码打印警告, +//! 我们可以使用编译器提供的 `dead_code` 属性来禁用警告, +//! +//! 在真实的工程中,你应该解决死代码的问题,而不是使用属性来禁用警告!!! +//! + +fn used_function() {} + +// `#[allow(dead_code)]` 属性可以禁用掉 `dead_code` 的代码检查 +#[allow(dead_code)] +fn unused_function() {} + +fn noisy_unused_function() {} +// FIXME ^ 尝试添加属性来禁用警告 + +fn main() { + used_function(); +} diff --git a/13.Attributes/13.2 Crates/main.rs b/13.Attributes/13.2 Crates/main.rs new file mode 100644 index 0000000..fa7d651 --- /dev/null +++ b/13.Attributes/13.2 Crates/main.rs @@ -0,0 +1,25 @@ +//! +//! 属性定义还可以额外指定包的属性,比如说包名称,包的类型。 +//! + +// 通过属性指明当前的包是一个库 +#![crate_type = "lib"] +// 通过属性指明当前的包的名称 +#![crate_name = "rary"] + +// 上面两个属性声明了当前的包的名称和类型, +// 这样使用 `rustc` 编译的时候就不需要手动指明类型和名称了。 + +pub fn public_function() { + println!("called rary's `public_function()`"); +} + +fn private_function() { + println!("called rary's `private_function()`"); +} + +pub fn indirect_access() { + print!("called rary's `indirect_access()`, that\n> "); + + private_function(); +} diff --git a/13.Attributes/13.3 cfg/Cargo.lock b/13.Attributes/13.3 cfg/Cargo.lock new file mode 100644 index 0000000..eec3a1f --- /dev/null +++ b/13.Attributes/13.3 cfg/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cfg" +version = "0.1.0" diff --git a/13.Attributes/13.3 cfg/Cargo.toml b/13.Attributes/13.3 cfg/Cargo.toml new file mode 100644 index 0000000..a45f9b3 --- /dev/null +++ b/13.Attributes/13.3 cfg/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "cfg" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/13.Attributes/13.3 cfg/src/main.rs b/13.Attributes/13.3 cfg/src/main.rs new file mode 100644 index 0000000..d492e71 --- /dev/null +++ b/13.Attributes/13.3 cfg/src/main.rs @@ -0,0 +1,38 @@ +//! `cfg` 属性和宏 +//! +//! `cfg` 是一个条件属性,当条件成立的时候才会生效,常用的两种形式如下。 +//! +//! - `cfg` 当做属性来使用:`#[cfg(...)]` 指定的位置 +//! - `cfg` 当做条件计算来用:`cfg!(...)` 条件表达式 +//! +//! 当做属性来使用的时候是在编译阶段执行的,当做条件表达式的时候是在运行时执行的。 +//! 两种形式都接受相同的参数类型。 +//! +//! `cfg!` 宏不像是 `#[cfg]`在编译阶段直接移除掉了条件为 `false` 的代码, +//! 而是保留了所有的代码在运行时执行判断。 +//! + +// 这个函数只会在 linux 系统下才会被编译到可执行文件中 +#[cfg(target_os = "linux")] +fn are_you_on_linux() { + println!("You are running linux!"); +} + +// 这个函数会在不是 linux 系统的任意系统下都会编译到可执行文件中 +#[cfg(not(target_os = "linux"))] +fn are_you_on_linux() { + println!("You are *not* running linux!"); +} + +fn main() { + are_you_on_linux(); + + println!("Are you sure?"); + + // 不管编译的系统是什么,条件分支的代码都会被编译到可执行文件中 + if cfg!(target_os = "linux") { + println!("Yes. It's definitely linux!"); + } else { + println!("Yes. It's definitely *not* linux!"); + } +} diff --git a/13.Attributes/13.4 Custom/custom b/13.Attributes/13.4 Custom/custom new file mode 100755 index 0000000..d0d82a6 Binary files /dev/null and b/13.Attributes/13.4 Custom/custom differ diff --git a/13.Attributes/13.4 Custom/custom.rs b/13.Attributes/13.4 Custom/custom.rs new file mode 100644 index 0000000..b98a5ee --- /dev/null +++ b/13.Attributes/13.4 Custom/custom.rs @@ -0,0 +1,14 @@ +//! cfg 还支持自定义属性值 +//! 通过 `rustc --cfg custom` 传递额外的值 +//! + +// 这个属性就是外部传递的 +// rustc --cfg some_condition custom.rs && ./custom +#[cfg(some_condition)] +fn conditional_function() { + println!("condition met!"); +} + +fn main() { + conditional_function(); +} diff --git a/14.Generics/14. generics/Cargo.lock b/14.Generics/14. generics/Cargo.lock new file mode 100644 index 0000000..2686337 --- /dev/null +++ b/14.Generics/14. generics/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "generics" +version = "0.1.0" diff --git a/14.Generics/14. generics/Cargo.toml b/14.Generics/14. generics/Cargo.toml new file mode 100644 index 0000000..2a85b1d --- /dev/null +++ b/14.Generics/14. generics/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "generics" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/14.Generics/14. generics/src/main.rs b/14.Generics/14. generics/src/main.rs new file mode 100644 index 0000000..e8383cc --- /dev/null +++ b/14.Generics/14. generics/src/main.rs @@ -0,0 +1,46 @@ +//! +//! 泛型(Generics) +//! +//! 泛型是将类型和功能可以用在更多的类型上的一个能力, +//! 它可以通过多种方式非常有效的缩减重复代码,但是带来的问题是会让使用方的语法更复杂。 +//! +//! 作为泛型需要非常精确的指定泛型可接受的类型范围。 +//! 泛型最简单常用的地方就是当做函数的参数使用了。 +//! +//! 泛型函数可以通过 `` (T可以是任意的名称)来声明接收泛型类型,泛型类型需要使用大驼峰命名 ``, +//! 如果一个函数声明了泛型参数,并且参数指明使用了泛型中声明的类型,则该函数就是泛型函数, +//! 没有使用泛型声明的参数则是具体类型。 +//! +//! 下面这个就是一个泛型的函数 +//! ```r +//! fn foo(arg: T) { ... } +//! ``` +//! 该函数定义了一个泛型 `T`,声明参数 `arg` 为泛型 `T`。 +//! 这个函数可以接受任意类型的参数。 +//! + +// 一个具体类型 `A` +struct A; + +// 定义一个类型 `Single`,该类型拥有一个元组(Tuple),这个元组里面有一个类型 `A` (这个 `A` 类型就是上面定义的类型) +struct Single(A); +// ^ 这里定义元组内只包含一个 `A` 类型 + +// 这里声明了一个泛型 ``,表明 `SingleGen` 类型是一个泛型的类型, +// 然后这个泛型 `T`,放在了元组中。 +// 这里泛型 `` 中的 `T` 可以是任意的类型,包含上面定义的 `A` +struct SingleGen(T); + +fn main() { + // `Single` 类型需要一个类型 `A`,因为 `Single` 是具体类型的类型,不是泛型。 + let _s = Single(A); + + // 创建一个变量 `_char` 明确指明类型是 `SingleGen`, + // 这里通过 `SingleGen('a')` 明确的向泛型传递了一个 `char` 型的参数。 + let _char: SingleGen = SingleGen('a'); + + // `SingleGen` 也可以使用任意的其他类型,泛型可以自动根据参数推断具体的类型。 + let _t = SingleGen(A); // `A` 是上面定义的类型。 + let _i32 = SingleGen(6); // 使用 `i32` 类型。 + let _char = SingleGen('a'); // 使用 `char` 类型。 +} diff --git a/14.Generics/14.1 Functions/Cargo.lock b/14.Generics/14.1 Functions/Cargo.lock new file mode 100644 index 0000000..82f83a1 --- /dev/null +++ b/14.Generics/14.1 Functions/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "functions" +version = "0.1.0" diff --git a/14.Generics/14.1 Functions/Cargo.toml b/14.Generics/14.1 Functions/Cargo.toml new file mode 100644 index 0000000..a9b5578 --- /dev/null +++ b/14.Generics/14.1 Functions/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "functions" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/14.Generics/14.1 Functions/src/main.rs b/14.Generics/14.1 Functions/src/main.rs new file mode 100644 index 0000000..71efcc0 --- /dev/null +++ b/14.Generics/14.1 Functions/src/main.rs @@ -0,0 +1,41 @@ +//! +//! 泛型函数 +//! +//! 泛型的相关规则也可以直接使用在函数上,当一个类型 `T` 被声明成了 ``,则该类型就是泛型。 +//! +//! 如果编译器不能推断出泛型的参数或者泛型的返回值的情况下,需要明确的指出泛型的类型。 +//! + +struct A; // 具体类型 `A` +struct S(A); // 具体类型 `S` +struct SGen(T); // 泛型类型 `SGen`. + +// 下面这些函数都会直接转移变量的所有权,一旦调用了函数,变量就被转移到函数中了,而函数中什么都没做 +// 当函数结束的时候会直接回收掉所有传入的参数。 + +// `reg_fn` 函数接受一个具体的类型 `S`,所以该函数不是泛型的。 +fn reg_fn(_s: S) {} + +// `gen_spec_t` 函数接受一个 `SGen`,虽然 `SGen` 是泛型, +// 但是我们手动指定了 `SGen` 的类型为 `A`,所以该函数也不是泛型的 +fn gen_spec_t(_s: SGen) {} + +// 这里同上,因为手动指定了 `SGen` 的类型是 `i32` 所以该函数也不是泛型的。 +fn gen_spec_i32(_s: SGen) {} + +// `generic` 函数通过 声明了一个泛型 `T` 的类型,然后把这个泛型 `T`,传递给了 `SGen`, +// 所以该函数是一个拥有一个泛型类型的泛型函数。 +fn generic(_s: SGen) {} + +fn main() { + // 使用非泛型的函数 + reg_fn(S(A)); // 具体类型 + gen_spec_t(SGen(A)); // 隐式指定 `SGen` 的泛型类型为 `A`。 + gen_spec_i32(SGen(6)); // 隐式指定 `SGen` 的泛型类型为 `i32`。 + + // 手动明确指定函数的泛型类型为 `char` 类型 + generic::(SGen('a')); + + // 隐式指定泛型类型为 `char` 类型 + generic(SGen('c')); +} diff --git a/14.Generics/14.2 Implementation/Cargo.lock b/14.Generics/14.2 Implementation/Cargo.lock new file mode 100644 index 0000000..010f4b3 --- /dev/null +++ b/14.Generics/14.2 Implementation/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "implementation" +version = "0.1.0" diff --git a/14.Generics/14.2 Implementation/Cargo.toml b/14.Generics/14.2 Implementation/Cargo.toml new file mode 100644 index 0000000..a854576 --- /dev/null +++ b/14.Generics/14.2 Implementation/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "implementation" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/14.Generics/14.2 Implementation/src/main.rs b/14.Generics/14.2 Implementation/src/main.rs new file mode 100644 index 0000000..45d8e43 --- /dev/null +++ b/14.Generics/14.2 Implementation/src/main.rs @@ -0,0 +1,45 @@ +//! +//! 泛型实现(implementation) +//! +//! 和函数类似,实现也可以使用泛型 +//! +//! ``` +//! struct S; // 具体类型 `S` +//! struct GenericVal(T); // 泛型 `GenericVal` +// +//! // 使用具体类型实现 GenericVal 类型 +//! impl GenericVal {} // 对于 `f32` 类型的实现 +//! impl GenericVal {} // 对于 `S` 类型的实现 +//! +//! // `` 继续保持示范性,也就是任意类型的实现 +//! impl GenericVal {} +//! ``` +//! +struct Val { + val: f64, +} + +struct GenVal { + gen_val: T, +} + +// 具体类型的实现 +impl Val { + fn value(&self) -> &f64 { + &self.val + } +} + +// 泛型类型对于任意的类型的实现 +impl GenVal { + fn value(&self) -> &T { + &self.gen_val + } +} + +fn main() { + let x = Val { val: 3.0 }; + let y = GenVal { gen_val: 3i32 }; + + println!("{}, {}", x.value(), y.value()); +} diff --git a/14.Generics/14.3 Traits/Cargo.lock b/14.Generics/14.3 Traits/Cargo.lock new file mode 100644 index 0000000..0b7d36b --- /dev/null +++ b/14.Generics/14.3 Traits/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "traits" +version = "0.1.0" diff --git a/14.Generics/14.3 Traits/Cargo.toml b/14.Generics/14.3 Traits/Cargo.toml new file mode 100644 index 0000000..460f45f --- /dev/null +++ b/14.Generics/14.3 Traits/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "traits" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/14.Generics/14.3 Traits/src/main.rs b/14.Generics/14.3 Traits/src/main.rs new file mode 100644 index 0000000..8cb50c4 --- /dev/null +++ b/14.Generics/14.3 Traits/src/main.rs @@ -0,0 +1,34 @@ +//! +//! 泛型特性 (trait) +//! +//! 特性也可以是泛型的,下面是一个泛型特性的示例。 +//! + +struct Empty; +struct Null; + +// 定义一个泛型的特性 +trait DoubleDrop { + // 函数会转移实例的所有权,并且接收的参数也会转移所有权, + // 当函数执行以后,会自动移除实例和传入的参数 + fn double_drop(self, _: T); +} + +// 声明两个泛型类型 `T`、`U`,然后为泛型 `U` 实现上面定义的泛型 `DoubleDrop` 特性 +// 因为泛型 `T` 和 `U` 都没有明确的类型也就是可以是任意的类型,也就是说这个实现会对所有的类型都生效!!! +impl DoubleDrop for U { + // 该函数转移实例和参数的所有权,然后直接丢弃,这样就实现了 `drop` 的功能。 + fn double_drop(self, _: T) {} +} + +fn main() { + let empty = Empty; + let null = Null; + + // 丢弃了 `empty` 和 `null` 两个实例。 + empty.double_drop(null); + + // empty; + // null; + // ^ TODO: 尝试移除注释 +} diff --git a/14.Generics/14.4 Bounds/Cargo.lock b/14.Generics/14.4 Bounds/Cargo.lock new file mode 100644 index 0000000..ccf6e84 --- /dev/null +++ b/14.Generics/14.4 Bounds/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "bounds" +version = "0.1.0" diff --git a/14.Generics/14.4 Bounds/Cargo.toml b/14.Generics/14.4 Bounds/Cargo.toml new file mode 100644 index 0000000..171166b --- /dev/null +++ b/14.Generics/14.4 Bounds/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "bounds" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/14.Generics/14.4 Bounds/src/main.rs b/14.Generics/14.4 Bounds/src/main.rs new file mode 100644 index 0000000..a8d66b3 --- /dev/null +++ b/14.Generics/14.4 Bounds/src/main.rs @@ -0,0 +1,112 @@ +//! +//! 泛型约束(Bounds) +//! +//! 再使用泛型的时候,很多时候都会需要要求泛型必须拥有哪些特性,如果不能满足的话不能使用该函数, +//! 所有就有了泛型的约束,要求传递的参数必须实现某些特定的特性。 +//! + +fn example01() { + use std::fmt::Display; + + // 该函数定义了一个泛型类型 `T`,并且约束该类型必须实现要先实现 `Display` 特性。 + fn printer(t: T) { + println!("{}", t); + } + + struct S(T); + + // 错误! `Vec` 没有实现 `Display` 特性 + // let s = S(vec![1]); + // 移除注释查看错误 +} + +// 导入格式化标签 `{:?}` 必须要实现的特性。 +use std::fmt::Debug; + +trait HasArea { + fn area(&self) -> f64; +} + +impl HasArea for Rectangle { + fn area(&self) -> f64 { + self.length * self.height + } +} + +#[derive(Debug)] +struct Rectangle { + length: f64, + height: f64, +} +#[allow(dead_code)] +struct Triangle { + length: f64, + height: f64, +} + +// 泛型 `T` 必须实现 `Debug` 特性 +fn print_debug(t: &T) { + println!("{:?}", t); +} + +// 泛型 `T` 必须实现 `HasArea` 特性,只有实现了这个特性才能使用 `HasArea` 上的 `area` 方法。 +fn area(t: &T) -> f64 +where + // 另外一种泛型约束的语法,这样可以保持函数签名是干净的 + T: HasArea, +{ + t.area() +} + +/// 即使是空的特性也可以用作约束,就像是标准库提供的 `Copy`、`Eq` 一样 +fn testcase_empty_bounds() { + struct Cardinal; + struct BlueJay; + struct Turkey; + + trait Red {} + trait Blue {} + + impl Red for Cardinal {} + impl Blue for BlueJay {} + + // 这个函数只允许实现了 `Red` 特性的参数调用。 + fn red(_: &T) -> &'static str { + "red" + } + + // 这个函数只允许实现了 `Blue` 特性的参数调用。 + fn blue(_: &T) -> &'static str { + "blue" + } + + let cardinal = Cardinal; + let blue_jay = BlueJay; + let _turkey = Turkey; + + // `red()` 不能传递 `blue_jay`,因为函数进行了约束,反过来也一样。 + println!("A cardinal is {}", red(&cardinal)); + println!("A blue jay is {}", blue(&blue_jay)); + + // println!("A turkey is {}", red(&_turkey)); + // ^ TODO: 移除注释查看错误 +} + +fn main() { + let rectangle = Rectangle { + length: 3.0, + height: 4.0, + }; + let _triangle = Triangle { + length: 3.0, + height: 4.0, + }; + + print_debug(&rectangle); + println!("Area: {}", area(&rectangle)); + + // print_debug(&_triangle); + // println!("Area: {}", area(&_triangle)); + // ^ TODO: 移除注释查看错误 + // | Error: 这两个都没有实现相关的 `Debug` 或 `HasArea` 特性。 +} diff --git a/14.Generics/14.5 Multiple bounds/Cargo.lock b/14.Generics/14.5 Multiple bounds/Cargo.lock new file mode 100644 index 0000000..4014f2a --- /dev/null +++ b/14.Generics/14.5 Multiple bounds/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "multiple_bounds" +version = "0.1.0" diff --git a/14.Generics/14.5 Multiple bounds/Cargo.toml b/14.Generics/14.5 Multiple bounds/Cargo.toml new file mode 100644 index 0000000..b91a5ea --- /dev/null +++ b/14.Generics/14.5 Multiple bounds/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "multiple_bounds" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/14.Generics/14.5 Multiple bounds/src/main.rs b/14.Generics/14.5 Multiple bounds/src/main.rs new file mode 100644 index 0000000..eb4dc18 --- /dev/null +++ b/14.Generics/14.5 Multiple bounds/src/main.rs @@ -0,0 +1,30 @@ +//! +//! 多重泛型约束 +//! 多重约束可以在每个约束之间使用 `+` 号连接, +//! 多个泛型之间使用 `,` 号间隔 +//! +//! 同样支持在 `` 或者 where `T: bound + bound2` 中使用 +//! +use std::fmt::{Debug, Display}; + +fn compare_prints(t: &T) { + println!("Debug: `{:?}`", t); + println!("Display: `{}`", t); +} + +fn compare_types(t: &T, u: &U) { + println!("t: `{:?}`", t); + println!("u: `{:?}`", u); +} + +fn main() { + let string = "words"; + let array = [1, 2, 3]; + let vec = vec![1, 2, 3]; + + compare_prints(&string); + // compare_prints(&array); + // TODO ^ 移除注释查看错误 + + compare_types(&array, &vec); +} diff --git a/14.Generics/14.6 Where clauses/Cargo.lock b/14.Generics/14.6 Where clauses/Cargo.lock new file mode 100644 index 0000000..d60ba4d --- /dev/null +++ b/14.Generics/14.6 Where clauses/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "where_clauses" +version = "0.1.0" diff --git a/14.Generics/14.6 Where clauses/Cargo.toml b/14.Generics/14.6 Where clauses/Cargo.toml new file mode 100644 index 0000000..26a0c14 --- /dev/null +++ b/14.Generics/14.6 Where clauses/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "where_clauses" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/14.Generics/14.6 Where clauses/src/main.rs b/14.Generics/14.6 Where clauses/src/main.rs new file mode 100644 index 0000000..b4b1979 --- /dev/null +++ b/14.Generics/14.6 Where clauses/src/main.rs @@ -0,0 +1,29 @@ +//! +//! where 子语句 +//! 在声明泛型类型的时候可以通过 `where` 子语句把泛型约束从签名中提取出来, +//! 这样可以让声明看起来更简洁一些,而且 `where` 子语句可以限定任意的类型,而不仅仅是函数参数。 +//! +use std::fmt::Debug; + +trait PrintInOption { + fn print_in_option(self); +} + +// 这里使用 `where` 子语句重新定义了 `T` 类型为 `Option`。 +// +impl PrintInOption for T +where + Option: Debug, // 这里使用了类型的重定义,把 `T` 重新定义成 `Option` 然后对对重定义的类型进行约束 +{ + // 这里的 `self` 就是 `T` 的实例,然后使用 `Some` 包装 `self` 就可以打印了, + // 因为 `Some` 实现了 `Debug` 特性。 + fn print_in_option(self) { + println!("{:?}", Some(self)); + } +} + +fn main() { + let vec = vec![1, 2, 3]; + + vec.print_in_option(); +} diff --git a/14.Generics/14.7 newtype idiom/Cargo.lock b/14.Generics/14.7 newtype idiom/Cargo.lock new file mode 100644 index 0000000..4bd7f49 --- /dev/null +++ b/14.Generics/14.7 newtype idiom/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "new_type_idiom" +version = "0.1.0" diff --git a/14.Generics/14.7 newtype idiom/Cargo.toml b/14.Generics/14.7 newtype idiom/Cargo.toml new file mode 100644 index 0000000..03adeaa --- /dev/null +++ b/14.Generics/14.7 newtype idiom/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "new_type_idiom" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/14.Generics/14.7 newtype idiom/src/main.rs b/14.Generics/14.7 newtype idiom/src/main.rs new file mode 100644 index 0000000..a8b72ab --- /dev/null +++ b/14.Generics/14.7 newtype idiom/src/main.rs @@ -0,0 +1,41 @@ +//! +//! 新类型 (newtype) +//! +//! 新类型是指使用元组结构体包装一个其他的类型,然后基于新包装的类型实现类型的扩展或约束, +//! 这样的好处是可以让 `api` 更加清晰,不会造成歧义。 +//! + +struct Years(i64); + +struct Days(i64); + +impl Years { + pub fn to_days(&self) -> Days { + Days(self.0 * 365) + } +} + +impl Days { + /// 舍弃不足的年份 + pub fn to_years(&self) -> Years { + Years(self.0 / 365) + } +} + +fn old_enough(age: &Years) -> bool { + age.0 >= 18 +} + +fn main() { + let age = Years(5); + let age_days = age.to_days(); + println!("Old enough {}", old_enough(&age)); + println!("Old enough {}", old_enough(&age_days.to_years())); + // println!("Old enough {}", old_enough(&age_days)); + + { + let years = Years(42); + let years_as_primitive_1: i64 = years.0; // 访问元组数据 + let Years(years_as_primitive_2) = years; // 解构元组数据 + } +} diff --git a/14.Generics/14.8 Associated items/Cargo.lock b/14.Generics/14.8 Associated items/Cargo.lock new file mode 100644 index 0000000..9b9b9d3 --- /dev/null +++ b/14.Generics/14.8 Associated items/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "associated_items" +version = "0.1.0" diff --git a/14.Generics/14.8 Associated items/Cargo.toml b/14.Generics/14.8 Associated items/Cargo.toml new file mode 100644 index 0000000..ee928a9 --- /dev/null +++ b/14.Generics/14.8 Associated items/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "associated_items" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/14.Generics/14.8 Associated items/src/main.rs b/14.Generics/14.8 Associated items/src/main.rs new file mode 100644 index 0000000..3fdc1ff --- /dev/null +++ b/14.Generics/14.8 Associated items/src/main.rs @@ -0,0 +1,122 @@ +//! +//! 关联项是指与各种类型的项目有关的一组规则, +//! 他是泛型特征的扩展,允许泛型特征在内部自定义新的项。 +//! +//! 其中有一种成为关联类型,也就是实现特征的时候需要明确指定特征上定义的项的类型。 +//! + +fn the_problem() { + struct Container(i32, i32); + + // 该特性明确要求需要传递 `` 两个泛型类型, + // 还有两个方法可以获取 `first`,`last` 两个值。 + trait Contains { + fn contains(&self, _: &A, _: &B) -> bool; // 明确指明需要泛型 `A`, `B` + fn first(&self) -> i32; // 不需要泛型 `A` 或 `B`. + fn last(&self) -> i32; // 不需要泛型 `A` 或 `B`. + } + + // 明确声明要实现的类型是 `Contains`,这样就代表泛型特性的 `` 就是 `` + impl Contains for Container { + fn contains(&self, number_1: &i32, number_2: &i32) -> bool { + (&self.0 == number_1) && (&self.1 == number_2) + } + + fn first(&self) -> i32 { + self.0 + } + + fn last(&self) -> i32 { + self.1 + } + } + + // 问题是这里的三个泛型的之间的关系, + // 这里 C 被约束为 Contains, 而我们写约束的时候很麻烦。 + fn difference(container: &C) -> i32 + where + C: Contains, + { + container.last() - container.first() + } + + let number_1 = 3; + let number_2 = 10; + + let container = Container(number_1, number_2); + + println!( + "Does container contain {} and {}: {}", + &number_1, + &number_2, + container.contains(&number_1, &number_2) + ); + println!("First number: {}", container.first()); + println!("Last number: {}", container.last()); + + println!("The difference is: {}", difference(&container)); +} + +fn associated_types() { + struct Container(i32, i32); + + // 还是相同的定义,只不过这里不再使用泛型。 + trait Contains { + // 定义两个通用的关联类型 `A`,`B`,这个时候还不知道关联类型的具体类型是什么, + // 具体可能是什么类型是由实现该特性的地方定义的。 + type A; + type B; + + // 这里 `Self::A` 中的 `Self` 实际上是指的 `Contains`,也就是当前的特性。 + // 到实际的实现的时候这个 `Self` 会被替换成实现该特性的实际类型。 + fn contains(&self, _: &Self::A, _: &Self::B) -> bool; + fn first(&self) -> i32; + fn last(&self) -> i32; + } + + impl Contains for Container { + // 明确指定特性中定义的关联类型的具体类型。 + type A = i32; + type B = i32; + + // 这里的 `number_1: &i32, number_2: &i32` 可以被替换成 `number_1: &Self::A, number_2: &Self::B`, + // 因为 `Self` 等同于 `Container` + fn contains(&self, number_1: &i32, number_2: &i32) -> bool { + (&self.0 == number_1) && (&self.1 == number_2) + } + + fn first(&self) -> i32 { + self.0 + } + + fn last(&self) -> i32 { + self.1 + } + } + + // 这里进行约束的时候就不需要额外的声明 `Contains` 特性所需要的两个泛型类型了。 + fn difference(container: &C) -> i32 { + container.last() - container.first() + } + + let number_1 = 3; + let number_2 = 10; + + let container = Container(number_1, number_2); + + println!( + "Does container contain {} and {}: {}", + &number_1, + &number_2, + container.contains(&number_1, &number_2) + ); + println!("First number: {}", container.first()); + println!("Last number: {}", container.last()); + + println!("The difference is: {}", difference(&container)); +} + +fn main() { + the_problem(); + associated_types(); +} diff --git a/14.Generics/14.9 Phantom type parameters/Cargo.lock b/14.Generics/14.9 Phantom type parameters/Cargo.lock new file mode 100644 index 0000000..3aec20c --- /dev/null +++ b/14.Generics/14.9 Phantom type parameters/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "phantom_type_parameters" +version = "0.1.0" diff --git a/14.Generics/14.9 Phantom type parameters/Cargo.toml b/14.Generics/14.9 Phantom type parameters/Cargo.toml new file mode 100644 index 0000000..e229da4 --- /dev/null +++ b/14.Generics/14.9 Phantom type parameters/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "phantom_type_parameters" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/14.Generics/14.9 Phantom type parameters/src/main.rs b/14.Generics/14.9 Phantom type parameters/src/main.rs new file mode 100644 index 0000000..a335698 --- /dev/null +++ b/14.Generics/14.9 Phantom type parameters/src/main.rs @@ -0,0 +1,58 @@ +//! +//! 幽灵类型 (Phantom type) +//! +//! 幽灵类型的特性是只存在于编译阶段用于辅助编译器推断类型和强制保持某些变量,属性的生命周期。 +//! +//! 有很多时候需要和外部的三方库交换数据,这时候我们把不安全的代码封装到安全的结构或者代码中的时候, +//! 需要交换很多数据,这些数据由三方的库申请的,这时候编译器并不知道这个数据的生命周期,所以需要这个 +//! 幽灵类型来明确的告知编译器这个类型的生命周期,这样编译器才能通过编译检查。 +//! +//! 关于幽灵类型更加详细的解释可以看[这里](https://doc.rust-lang.org/nomicon/phantom-data.html) +//! +//! 幽灵定义在泛型中,通过标准库导出的特殊类型 `std::marker::PhantomData` 来标记一个幽灵类型, +//! 该类型只用于编译器的类型检查,并不占用空间,也不能用于存放数据,也没有运行时的行为。 +//! +//! + +use std::marker::PhantomData; // 导入幽灵类型 + +// 声明一个拥有幽灵类型的元组结构,两个泛型 `` 泛型 `A` 拥有是常规的类型,泛型 `B` 是一个隐藏的幽灵类型。 +#[derive(PartialEq)] // 允许结构直接行进 `==` 运算 +struct PhantomTuple(A, PhantomData); + +// 声明一个拥有幽灵类型的结构体,两个泛型 `` 泛型 `A` 拥有是常规的类型,泛型 `B` 是一个隐藏的幽灵类型。 +#[derive(PartialEq)] // 允许结构直接行进 `==` 运算 +struct PhantomStruct { + first: A, + phantom: PhantomData, +} + +// 注意:代码编译的时候只会为正常的泛型 `A` 申请空间,而幽灵类型 `B` 没有空间, +// 所以使用了幽灵类型 `B` 的字段或者位置都不能当做实际存在的类型使用。 + +fn main() { + // 这里的 `f32` 和 `f64` 类型是给幽灵类型使用的。 + // PhantomTuple type specified as ``. + // 明确指定泛型的数据类型,这里声明类型为 `` + let _tuple1: PhantomTuple = PhantomTuple('Q', PhantomData); + // 明确指定泛型的数据类型,这里声明类型为 `` + let _tuple2: PhantomTuple = PhantomTuple('Q', PhantomData); + + // 明确指明类型 `` + let _struct1: PhantomStruct = PhantomStruct { + first: 'Q', + phantom: PhantomData, + }; + + // 明确指明类型 `` + let _struct2: PhantomStruct = PhantomStruct { + first: 'Q', + phantom: PhantomData, + }; + + // 编译错误!类型匹配不能对比 + // println!("_tuple1 == _tuple2 yields: {}", _tuple1 == _tuple2); + + // 编译错误!类型匹配不能对比 + // println!("_struct1 == _struct2 yields: {}", _struct1 == _struct2); +} diff --git a/9.Functions/src/main.rs b/9.Functions/src/main.rs index 776de8f..f833292 100644 --- a/9.Functions/src/main.rs +++ b/9.Functions/src/main.rs @@ -1,3 +1,5 @@ +#![feature(never_type)] // 强制启用内置的 `!` 类型 + //! //! # Functions //! 函数使用 `fn` 关键字定义。必须指定参数类型和数量, @@ -223,90 +225,497 @@ fn closures() { fn closures_capturing() { { use std::mem; - let color = String::from("green"); + let mut color = String::from("green"); - // A closure to print `color` which immediately borrows (`&`) `color` and - // stores the borrow and closure in the `print` variable. It will remain - // borrowed until `print` is used the last time. - // - // `println!` only requires arguments by immutable reference so it doesn't - // impose anything more restrictive. + // 这个闭包捕获了 `color` 变量,闭包内部使用 `println!` 宏打印变量 + // `println!` 宏只会对使用的变量 `借用一个不变引用` 也就是 `&color` + // 所以编译器推断 `color` 被闭包借用了一个不变引用, + // 当这个闭包被最后一次使用后,闭包内借用的不变引用就会被回收 + + // 因为 `println!` 只会对使用的变量借用一个不可变引用,所以没有对引用增加更多的检查规则 let print = || println!("`color`: {}", color); - // Call the closure using the borrow. - print(); + // 调用闭包 + // print(); - // `color` can be borrowed immutably again, because the closure only holds - // an immutable reference to `color`. + // 因为 Rust 允许同时存在多个不可变引用,变量 `color` 可以被再次借用。 let _reborrow = &color; + + // 再次调用闭包,这已经是最后一次使用闭包了,所以这次调用以后,闭包内的引用就会被回收 print(); - // A move or reborrow is allowed after the final use of `print` + // 因为 `color` 已经不存在其他的引用了,所以可以对变量的所有权进行转移。 + // 你可能好奇,上面明明还有一个借用的变量是 `_reborrow` ,为什么这里可以进行转移? + // 是因为 `_reborrow` 这个变量没有被使用,在下面这行代码之后 `_reborrow` 也会失效。 let _color_moved = color; let mut count = 0; - // A closure to increment `count` could take either `&mut count` or `count` - // but `&mut count` is less restrictive so it takes that. Immediately - // borrows `count`. - // - // A `mut` is required on `inc` because a `&mut` is stored inside. Thus, - // calling the closure mutates the closure which requires a `mut`. + + // 因为闭包内对外部变量进行了直接修改, + // 所以这个闭包需要声明成可变的 let mut inc = || { count += 1; println!("`count`: {}", count); }; - // Call the closure using a mutable borrow. + // 调用可变闭包 inc(); - // The closure still mutably borrows `count` because it is called later. - // An attempt to reborrow will lead to an error. + // 因为闭包还会再次使用,Rust 中当一个可变引用存在的话,就不允许再次使用其他的引用, + // 所以这里不能再次借用一个不可变的引用 // let _reborrow = &count; - // ^ TODO: try uncommenting this line. + // ^ TODO: 移除注释查看错误 + + // 再次调用闭包,因为这里是最后一次使用闭包,所以执行完闭包后,闭包就会被回收 inc(); - // The closure no longer needs to borrow `&mut count`. Therefore, it is - // possible to reborrow without an error + // 因为闭包已经被回收了,所以这里可以再次借用新的引用 let _count_reborrowed = &mut count; - // A non-copy type. + // `Box` 类型是在堆上储存数据,不像是原始值在赋值的过程中产生拷贝 let movable = Box::new(3); - // `mem::drop` requires `T` so this must take by value. A copy type - // would copy into the closure leaving the original untouched. - // A non-copy must move and so `movable` immediately moves into - // the closure. + // `mem:drop` 方法是一个泛型方法接收一个 `T` 类型,而且该方法会自动转移变量的所有权 + // 因为变量被闭包捕获的同时,也会对变量的所有权进行转移,一旦闭包被执行过一次 + // 原始的变量就会被移除掉,所以该闭包会被推断成 `FnOnce() ` 类型,并且只能执行一次, + // + // 但是如果变量实现了 `Copy` 特性的话,则会隐式的调用 `Copy` 特性来得到一个拷贝, + // 这样就可以让原始变量可以继续使用,而闭包本身也会自动推断成 `Fn()` 类型 let consume = || { println!("`movable`: {:?}", movable); mem::drop(movable); }; // `consume` consumes the variable so this can only be called once. + // 使用闭包 `consume` 进行变量转移,但是该方法只能调用一次,因为 Box 没有实现 `Copy` consume(); // consume(); - // ^ TODO: Try uncommenting this line. + // ^ TODO: 移除注释查看错误 } { - // `Vec` has non-copy semantics. + // `Vec` 也是一个堆储存的类型 let haystack = vec![1, 2, 3]; + // 闭包借用了 `haystack` 变量的一个不可变引用,但是因为 `move` 关键字, + // 会让编译器直接转移变量的所有权,也就是说,闭包直接转移了 `haystack` 变量的所有权, + // 因为所有权被转移到了闭包内部,所以在后面的代码中都不能使用该变量了 let contains = move |needle| haystack.contains(needle); println!("{}", contains(&1)); println!("{}", contains(&4)); // println!("There're {} elements in vec", haystack.len()); - // ^ Uncommenting above line will result in compile-time error - // because borrow checker doesn't allow re-using variable after it - // has been moved. + // 移除注释查看错误 + // 上面这行代码会报错,因为闭包直接转移了 `haystack` 变量的所有权 - // Removing `move` from closure's signature will cause closure - // to borrow _haystack_ variable immutably, hence _haystack_ is still - // available and uncommenting above line will not cause an error. + // 如果移除掉闭包前面的 `move` 关键字,则闭包只会借用一个不可变引用 + // 这样的话上面的代码就不会报错了。 } } +/// 闭包当做参数使用 +/// +/// 在 Rust 中使用闭包特性,大多数情况下都可以让类型推断引擎自动根据上下文推断闭包的类型, +/// 但是当闭包使用在函数的参数的时候,不允许这么做,一旦函数接受一个闭包的话,那就必须要明确指定闭包的类型 +/// +/// 闭包根据变量的限制规则,有三种类型限制,以下三种限制对于变量的使用限制依次递减 +/// - `Fn`: 闭包只能使用不可变引用 `&T` +/// - `FnMut`: 闭包可以使用可变引用 `&mut T` +/// - `FnOnce`: 闭包可以任意使用 `T`,因为直接使用变量会导致变量的所有权转移,所以一旦闭包捕获了变量的所有权,则该闭包只能调用一次 +/// +/// 编译器会自动检查被捕获变量的使用方式,并选择一个对于变量使用上限制最少的类型 +/// +/// 假设一个声明为 `FnOnce` 的参数,但实际上闭包可能引用了 `&T`,'&mut T`,`T`, +/// 编译器最终会根据实际闭包中引用的变量类型来决定闭包是什么类型的。 +/// +/// 因为如果一个变量可以被转移所有权(闭包内直接使用了 `T` 类型,或者闭包前面加上了 `move` 关键字), +/// 那么就代表这个变量可以被借用,但是反过来不行。 +/// +/// 如果函数参数要求一个 `Fn` 类型的闭包,那么闭包内就不能使用 `&mut T` 和 `T`。 +/// 因为 `Fn` 的要求是闭包内只能使用 `&T`。 +/// +fn as_input_parameters() { + // 尝试修改函数的闭包约束为 `Fn`,`FnMut`,`FnOnce` 来查看不同的结果 + + // 表明 `F` 是一个泛型的的参数,泛型参数由第一个调用方的参数类型来确定具体的类型 + // 函数接受一个闭包,闭包约束为 `FnOnce()` 类型, + // 该类型的闭包内可以使用 `&T`,`&mut T`,`T` + fn apply(f: F) + // where 语句用来约束泛型 `T` 必须是哪些可能的类型 + where + F: FnOnce(), // 这里约束 F 是一个只能够调用一次,并且没有参数和返回值的函数 + { + // ^ TODO: 尝试修改类型为 `Fn` 或 `FnMut`。 + f(); + } + + // 这个函数接收一个闭包函数,并返回一个 `i32` 的值。 + fn apply_to_3(f: F) -> i32 + where + // 闭包参数接受一个 `i32` 的参数,返回一个 `i32` 的值 + F: Fn(i32) -> i32, + { + f(3) + } + + use std::mem; + + let greeting = "hello"; + // 从借用的引用值通过 `to_owned()` 创建一个有所有权的值。 + let mut farewell = "goodbye".to_owned(); + + // 闭包会根据借用的变量,以及变量的使用情况来逐步的确认闭包的类型。 + // 该闭包因为借用了一个不可变变量 `&greeting`,和一个转移所有权的变量 `farewell`, + // 所以该闭包被推断为 `FnOnce` 类型 + let diary = || { + // 这里借用了一个不可变引用 `&greeting`,不可变引用的闭包类型是 `Fn`,所以这里推断闭包为 `Fn`。 + println!("I said {}.", greeting); + + // 这里又借用了一个可变引用 `&mut farewell`,可变引用的闭包类型是 `FnMut`,所以这里推断为 `FnMut`。 + farewell.push_str("!!!"); + // 因为 `farewell` 已经借用为了 `&mut farewell`,又因为 `&mut farewell` 包含 `&mut` 所以闭包依旧是 `FnMut`。 + println!("Then I screamed {}.", farewell); + println!("Now I can sleep. zzzzz"); + + // 这里因为 `mem::drop` 会要求转移参数 `farewell` 的所有权,所以闭包又被推断为 `FnOnce`。 + mem::drop(farewell); + }; + + // 把闭包通过参数传递给 `apply` 函数, + // 因为 `apply` 函数要求的正好就是 `FnOnce` 类型的闭包,所以这里可以正常调用 + apply(diary); + + // 这个闭包没有捕获任何的外部变量,所以这个闭包被推断为 `Fn` 类型 + // 闭包接受一个任意类型的参数,并返回参数 * 2的结果。 + // 这里闭包的参数和返回值被推断为 i32 类型的原因是, + // 我们把闭包传递给了函数 `apply_to_3`,而这个函数的泛型约束为 `Fn(i32) -> i32`, + // 编译器通过我们的代码行为来自动的为闭包分配了对应的类型。 + let double = |x| 2 * x; + + println!("3 doubled: {}", apply_to_3(double)); +} + +/// 闭包可以非常简洁的在声明的上下文作用域中自动捕获一些变量,那么这个简洁的能力带来了哪些问题呢? +/// 当闭包被当做参数传递的时候,函数声明必须是泛型,而且这个泛型必须约束为 `Fn`,`FnMut`,`FnOnce` 的一种 +/// +/// ``` +/// // 这里必须定义成泛型的函数,而且必须对 `f` 参数进行泛型约束 +/// fn apply(f: F) +/// where +/// F: FnOnce() +/// { +/// f(); +/// } +/// +/// ``` +/// +/// 当闭包被定义的时候,编译器隐式的创建了一个匿名的结构来保存这些被闭包捕获的变量, +/// 这个匿名的结构根据闭包的代码推断出应该实现 `Fn`,`FnMut`,`FnOnce` 的其中一个特性。 +/// 变量会自动的绑定到,该匿名的结构体实例,来保留捕获的变量,直到该闭包被调用。 +/// +/// 匿名的结构体在实例化的时候并不知道具体的类型是什么, +/// 所以类型引擎会通过闭包实例真正被使用的时候,来尝试推断出具体的类型。 +/// +fn type_anonymity() { + // 泛型 `F` 是一个闭包,该必须实现 `Fn()` 特性,也就是说 + // 传入的 `f` 参数必须是一个函数,该函数没有参数,并且没有返回值。 + fn apply(f: F) + where + F: Fn(), + { + f(); + } + + let x = 7; + + // 该闭包使用不可变引用捕获了 `x` 变量,并且没有返回值。 + // 编译器会自动生命一个匿名的结构体,该结构体上保存了 `x` 的不可变引用 + // 并且该结构体实现了 `Fn` 的特性。 + let print = || println!("{}", x); + + apply(print); +} + +/// 闭包可以当做参数传递给函数,那么常规函数是否也可以呢? +/// 答案是可以,只要函数的实现可以满足泛型 `Fn`,`FnMut`,`FnOnce` 的约束即可 +fn input_functions() { + // 定义一个泛型参数约束为一个闭包类型,该闭包可以引用外部的不可变变量 + fn call_me(f: F) { + f(); + } + + // 定义一个可以符合 `Fn` 特性约束的普通函数。 + fn function() { + println!("I'm a function!"); + } + + // 定义一个符合 `Fn` 特性约束的闭包。 + let closure = || println!("I'm a closure!"); + + // 传递闭包 + call_me(closure); + // 传递普通函数 + call_me(function); +} + +/// 闭包可以当做函数参数传递,那么闭包也可以当做函数返回值传递 +/// 因为匿名的闭包类型是由编译器自动定义的匿名类型,所以我们不可能提前知道匿名的类型是什么, +/// 但是我们可以知道匿名函数实现了什么特性比如说 `Fn`,`FnMut`,`FnOnce`, +/// 所以我们可以通过使用 `impl` 关键字来约束返回值的类型,来间接的使用闭包返回值。 +fn as_output_parameters() { + // 这里使用 `impl` 关键字约束返回值类型是 `Fn` + fn create_fn() -> impl Fn() { + let text = "Fn".to_owned(); + + // 这里必须要使用 `move` 关键字,因为一旦当前的函数结束以后 + // 这个函数中的所有变量都会被回收,因为闭包中捕获了变量 `text`, + // 所以我们需要让这个 `text` 强制转移到闭包的内部,来避免当前函数结束后 `text` 被回收的情况。 + move || println!("This is a: {}", text) + } + + fn create_fnmut() -> impl FnMut() { + let text = "FnMut".to_owned(); + + move || println!("This is a: {}", text) + } + + fn create_fnonce() -> impl FnOnce() { + let text = "FnOnce".to_owned(); + + move || println!("This is a: {}", text) + } + + let fn_plain = create_fn(); + let mut fn_mut = create_fnmut(); + let fn_once = create_fnonce(); + + fn_plain(); + fn_plain(); + + fn_mut(); + fn_mut(); + + fn_once(); + + // 下面这行代码会报错,因为 `fn_once` 的约束是 `FnOnce` 所以只能调用一次 + // fn_once(); + // 移除注释查看错误 +} + +/// 标准库 `Iterator::any` +/// 该函数会把迭代器中的所有元素都传递给闭包,闭包来返回一个 `bool` 值, +/// 如果有任意一个闭包的返回值是 `true`,则 `Iterator::any` 方法会返回 `true`,否则返回 `false` +/// +/// 该方法声明如下 +/// +/// ``` +/// pub trait Iterator { +/// // 传递进来的闭包函数接受的参数类型 +/// type Item; +/// +/// // `any` 接受一个 `&mut self` 的可变引用, +/// // 也就是说当前方法可以修改数据,但是只是修改而不是转移所有权 +/// fn any(&mut self, f: F) -> bool +/// where +/// // 传递的闭包函数接受一个参数,并且该闭包可以对参数进行修改 +/// F: FnMut(Self::Item) -> bool; +/// } +/// ``` +/// +/// +fn iterator_any() { + let vec1 = vec![1, 2, 3]; + let vec2 = vec![4, 5, 6]; + + // `iter()` 会生成一个对原始数据引用的一个迭代器,然后调用 `any` 方法传递了一个闭包 + // 因为 `any` 方法回调的时候会传递原始数据的引用 `&T`, + // 根据结构规则,`&T` 和 `&x` 会同时去掉 `&` 所以,闭包内部的 `x` 是 `i32` 类型 + println!("2 in vec1: {}", vec1.iter().any(|&x| x == 2)); + + // `into_iter()` 方法会直接转移 `vec2` 的所有权并把原始数据转换成迭代器, + // 所以这里 `any` 的闭包中不需要使用解构语法就可以拿到 `i32` 类型 + println!("2 in vec2: {}", vec2.into_iter().any(|x| x == 2)); + + // 因为 `iter()` 只是借用了 `vec1` 中的值,所以这里可以再次使用 `vec1`。 + println!("vec1 len: {}", vec1.len()); + println!("First element of vec1 is: {}", vec1[0]); + + // `into_iter()` 因为转移了 `vec2` 的所有权,所以这里不能再次使用 `vec2` 了 + // println!("First element of vec2 is: {}", vec2[0]); + // println!("vec2 len: {}", vec2.len()); + // TODO: 移除注释查看错误。 + + let array1 = [1, 2, 3]; + let array2 = [4, 5, 6]; + + // `iter()` 方法同样可以在数组中使用,这里依旧是借用了数据,所以需要解构。 + println!("2 in array1: {}", array1.iter().any(|&x| x == 2)); + // `into_iter()` 方法转移了数组的所有权,所以这里不需要解构,但是后面就不能再次使用原数组了。 + println!("2 in array2: {}", array2.into_iter().any(|x| x == 2)); +} + +/// 标准库 `Iterator::find` 方法 +/// 该方法接受一个 `&mut self` 引用,和一个闭包函数, +/// 当闭包返回 `true` 的时候,该函数会返回对应的传递给闭包的那一项数据。 +/// 闭包函数接收一个 `&Self::Item` 不可变引用,并返回一个 `bool` 值, +/// 当闭包返回 `true` 整个函数就会退出并返回值。 +/// +/// `Iterator::find` 定义如下 +/// ``` +/// pub trait Iterator { +/// // 闭包要接收的参数类型。 +/// type Item; +/// +/// // `find` 接受一个 `&mut self` 参数,通过 `Option` 来返回结果。 +/// fn find

(&mut self, predicate: P) -> Option where +/// // 闭包接受一个 `&Self::item` 的引用参数,并返回一个 `bool` 值。 +/// P: FnMut(&Self::Item) -> bool; +/// } +/// ```` +fn searching_through_iterators() { + let vec1 = vec![1, 2, 3]; + let vec2 = vec![4, 5, 6]; + + // `iter()` 创建一个不可变引用的迭代器,该迭代器中的数据类型是 `&i32`。 + let mut iter = vec1.iter(); + // `into_iter()` 把 `vec2` 转移到新创建的迭代器中,该迭代器的数据类型是 `i32`。 + let mut into_iter = vec2.into_iter(); + + // 因为 `iter()` 创建出的迭代器类型是 `&i32` 类型,而根据 `find` 函数定义, + // 会再次给 `&i32` 类型再次创建一个不可变引用,也就是 `&(&i32)` == `&&i32`, + // 然后根据解构规则,这里的闭包需要两个解构符号 `&&x` 来获取 `i32` 类型。 + println!("Find 2 in vec1: {:?}", iter.find(|&&x| x == 2)); + + // 因为 `into_iter()` 创建出的迭代器类型是 `i32` 类型,而根据 `find` 函数定义, + // 会给 `i32` 类型创建一个不可变引用,也就是 `&i32`, + // 然后根据解构规则,这里的闭包需要解构 `&x` 来获取 `i32` 类型。 + println!("Find 2 in vec2: {:?}", into_iter.find(|&x| x == 2)); + + let array1 = [1, 2, 3]; + let array2 = [4, 5, 6]; + + // `iter()` 数组上也是一样的规则,`iter()` 转换成 `&i32`, + // `find` 会再次添加一个引用变成 `&&i32`, + // 所以闭包中解构需要 `&&x` + println!("Find 2 in array1: {:?}", array1.iter().find(|&&x| x == 2)); + + // `into_iter()` 转移了数组的所有权到迭代器中,类型为 `i32`, + // `find` 会添加一个引用变成 `&i32`, + // 所以闭包中解构需要 `&x` + println!( + "Find 2 in array2: {:?}", + array2.into_iter().find(|&x| x == 2) + ); +} + +/// 高阶函数 +/// Rust 中为迭代器(Iterator),提供了很多高阶的函数式函数, +/// 因为迭代器本身的实现是惰性的,而且因为迭代器可以避免数组、切片的越界检查, +/// 因此迭代器的性能会比普通的循环语句效率要高的,因为通过 `[]` 访问数组或切片都会进行运行时的检查,会消耗更多的性能。 +/// +/// 所以在 Rust 中应该优先使用迭代器,而且 Rust 提供了很多内置的函数式方法来让代码逻辑更清晰。 +/// +/// 下面是同一个功能的两种代码实现。 +/// +fn higher_order_functions() { + fn is_odd(n: u32) -> bool { + n % 2 == 1 + } + + let upper = 1000; + println!( + "Find the sum of all the squared odd numbers under {}", + upper + ); + + // 传统 for 循环写法 + { + // 定义一个计数器变量 + let mut acc = 0; + // 声明一个无限长度的迭代器 + for n in 0.. { + // 把数字进行平方计算 + let n_squared = n * n; + + if n_squared >= upper { + // 如果当前的平方结果大于上限的话则跳出循环 + break; + } else if is_odd(n_squared) { + // 如果平方结果是奇数的话,计数器加一 + acc += n_squared; + } + } + println!("imperative style: {}", acc); + } + + // 迭代器函数式写法 + { + let sum_of_squared_odd_numbers: u32 = (0..) // 无限长度的迭代器 + .map(|n| n * n) // 对数字取平方 + .take_while(|&n_squared| n_squared < upper) // 平方结果到达上限的话 终止迭代器 + .filter(|&n_squared| is_odd(n_squared)) // 把平方的奇数保留下来 + .sum(); // 统计所有符合条件的数字数量 + println!("functional style: {}", sum_of_squared_odd_numbers); + } +} + +/// 分流函数是一个特殊的函数,该函数的返回值定义是 `!`, +/// `!` 关键字表示是一个空的类型(never\never_type),也就是说函数永远不会返回。 +/// +/// ``` +/// // 该函数永远没有返回值,因为 `panic!` 宏会直接终止进程!! +/// fn foo() -> ! { +/// panic!("This call never returns.") +/// } +/// ``` +/// `!` 和 `()` 不同,`!` 的所有可能的值都不存在,而 `()` 有一个可能的值是 `()` 本身。 +/// +/// +/// +fn diverging_functions() { + // 返回值是 `()` 类型 + fn some_fn() -> () { + () // 返回一个 `()` 的实例 + } + + // `_a` 的类型是 `()` + let _a: () = some_fn(); + + println!("This function returns and you can see this line."); + + // `panic!` 宏不会返回,也没有返回值。 + // let x: ! = panic!("This call never returns."); + // 因为 `panic!` 宏不会返回,所以这行代码永远不会执行。 + // println!("You will never see this line!"); + // 尝试移除注释查看结果 + + // `!` (never_type) 的另一个非常有用的场景就是 `match` 模式匹配中的流程控制语句 `continue`, + // `contiune` 语句会强制跳转代码到下一次迭代的起始点,也就是说 `continue` 语句的返回值就是 `!` + fn sum_odd_numbers(up_to: u32) -> u32 { + let mut acc = 0; + for i in 0..up_to { + // 这里 `match` 语句必须返回 `u32` 类型的数据。 + let addition: u32 = match i % 2 == 1 { + // The "i" variable is of type u32, which is perfectly fine. + // 这里的 `i` 是 `u32` 类型,所以可以直接使用。 + true => i, + // 这里使用了 `contiune` 关键字,而这个关键字会改变代码的执行流程, + // 因为改变了代码的执行流程,所以 `contiune` 永远都不会返回,也就不会违反类型检查规则。 + false => continue, + }; + acc += addition; + } + acc + } + println!( + "Sum of odd numbers up to 9 (excluding): {}", + sum_odd_numbers(9) + ); + + // 还有比如说 `loop` 关键字也不会返回值,还有 `exit` 方法不会返回值,或者说一个无限循环的函数也不会返回值,等等的一些场景。 +} + fn main() { // 常规函数 functions(); @@ -316,6 +725,22 @@ fn main() { // 基础闭包 closures(); - // 闭包捕获 + // 闭包捕获以及闭包的类型推断 closures_capturing(); + // 闭包捕获的原理 + type_anonymity(); + // 普通函数当做参数传递 + input_functions(); + // 闭包当做返回值使用 + as_output_parameters(); + + // 标准库的一些闭包例子 + iterator_any(); + searching_through_iterators(); + + // 高阶函数 + higher_order_functions(); + + // 分流函数 never_type + diverging_functions(); } diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..e69de29