commit d1b95f1096aa31e9254259d2c9cd2f89d26df893 Author: nap.liu Date: Fri Dec 8 19:41:26 2023 +0800 init code diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..400485b --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +**/target/ +.DS_Store diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..2535759 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "rust-analyzer.linkedProjects": [ + "./9.Functions/Cargo.toml" + ] +} diff --git a/1.Hello World/1.1 Comments/.gitignore b/1.Hello World/1.1 Comments/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/1.Hello World/1.1 Comments/.gitignore @@ -0,0 +1 @@ +/target diff --git a/1.Hello World/1.1 Comments/Cargo.lock b/1.Hello World/1.1 Comments/Cargo.lock new file mode 100644 index 0000000..c80a919 --- /dev/null +++ b/1.Hello World/1.1 Comments/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "Comments" +version = "0.1.0" diff --git a/1.Hello World/1.1 Comments/Cargo.toml b/1.Hello World/1.1 Comments/Cargo.toml new file mode 100644 index 0000000..519db82 --- /dev/null +++ b/1.Hello World/1.1 Comments/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "Comments" +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/1.Hello World/1.1 Comments/src/main.rs b/1.Hello World/1.1 Comments/src/main.rs new file mode 100644 index 0000000..cacb7e8 --- /dev/null +++ b/1.Hello World/1.1 Comments/src/main.rs @@ -0,0 +1,27 @@ +fn main() { + // 这个是单行注释的例子 + // 使用双正斜线开头 + // 所有写在双斜线后面的代码都不会被编译 + + // println!("Hello, world!"); + + // 尝试运行一下看看结果。然后删除上面这行代码的注释标记,再次尝试运行一下看看结果。 + + /* + * 这个是多行注释的例子,通常情况下更推荐使用单行注释。 + * 多行数值一般都是临时禁用掉某些代码。 + * /* 多行注释允许 /* 嵌套 */ 使用 */ 但是要注意嵌套必须成对出现! + * 多行只需要几个字符就能排除掉当前所有 main() 函数中的代码. + * /* /* /* 你可以自己试一试 */ */ */ + */ + + /* + 注意:上一个多行注释中开头的 `*` 号完全是为了统一风格(可以理解为好看)。 + 实际上并不是必须的 。 + */ + + /// 使用块注释可以很容易的操作代码中的表达式,而行注释做不到这一点 + /// 你可以尝试删除下面代码中的快注释来修改代码的执行结果 + let x = 5 + /* 90 + */ 5; + println!("x 是 10 或 100? x = {}", x); +} diff --git a/1.Hello World/1.2 Formatted print/.gitignore b/1.Hello World/1.2 Formatted print/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/1.Hello World/1.2 Formatted print/.gitignore @@ -0,0 +1 @@ +/target diff --git a/1.Hello World/1.2 Formatted print/Cargo.lock b/1.Hello World/1.2 Formatted print/Cargo.lock new file mode 100644 index 0000000..f5f1b3b --- /dev/null +++ b/1.Hello World/1.2 Formatted print/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "Formatted_Print" +version = "0.1.0" diff --git a/1.Hello World/1.2 Formatted print/Cargo.toml b/1.Hello World/1.2 Formatted print/Cargo.toml new file mode 100644 index 0000000..65d0e04 --- /dev/null +++ b/1.Hello World/1.2 Formatted print/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "Formatted_Print" +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/1.Hello World/1.2 Formatted print/src/main.rs b/1.Hello World/1.2 Formatted print/src/main.rs new file mode 100644 index 0000000..1b8efeb --- /dev/null +++ b/1.Hello World/1.2 Formatted print/src/main.rs @@ -0,0 +1,56 @@ +fn main() { + // 通常情况下,`{}` 会自动替换成任意的参数。这些参数会被转换成字符串。 + println!("{} days", 31); + + // 可以在 `{}` 中使用附加参数的序号来指定使用哪个参数来替换 `{}`。 + // 附加参数是第一个格式化字符串后面跟随的参数,附加参数的索引从0开始计算。 + println!("{0}, this is {1}. {1}, this is {0}", "Alice", "Bob"); + + // 附加参数可以手动指定名称。 + println!( + "{subject} {verb} {object}", + object = "the lazy dog", + subject = "the quick brown fox", + verb = "jumps over" + ); + + // 可以在 `{}` 中使用 `:` 来指定转换的格式。 + println!("Base 10 (十进制): {}", 69420); // 69420 + println!("Base 2 (二进制): {:b}", 69420); // 10000111100101100 + println!("Base 8 (八进制): {:o}", 69420); // 207454 + println!("Base 16 (小写十六进制): {:x}", 69420); // 10f2c + println!("Base 16 (大写十六进制): {:X}", 69420); // 10F2C + + // 可以指定固定宽度,并且从左侧自动填充空白文本 + // 下面这个例子会输出 " 1",(四个空格和一个"1",总长度是5)。 + println!("{number:>5}", number = 1); + + // 可以手动指定一个字符用于填充剩余长度。 + println!("{number:0>5}", number = 1); // 00001 + // 同样可以通过使用 `<` 来使用右侧填充,这样的话输出就是 "10000"。 + println!("{number:0<5}", number = 1); // 10000 + + // 还可以使用命名参数来指定填充长度。 + // 只需要在命名参数后面添加 `$` 符号来使用命名参数。 + println!("{number:0>width$}", number = 1, width = 5); + + // Rust还会检查你传入的参数数量和格式化字符串中使用的数量是否匹配。 + println!("My name is {0}, {1} {0}", "Bond"); + // FIXME ^ 添加缺失的参数: "James" + + // 只有实现了 fmt::Display 的类型才能通过 `{}` 来格式化 + // 用户自定义的类型默认没有实现 fmt::Display. + + #[allow(dead_code)] // 禁用 `dead_code` 未使用代码的编译警告 + struct Structure(i32); + + // 因为 `Structure` 没有实现 fmt::Display 所以不能被编译。 + // println!("This struct `{}` won't print...", Structure(3)); + // TODO ^ 尝试取消注释查看编译错误。 + + // 从 Rust 1.58 版本开始,你可以直接在格式化字符串中使用变量。 + // 这个会输出 " 1",4个空格和一个 "1"。 + let number: f64 = 1.0; + let width: usize = 5; + println!("{number:>width$}"); +} diff --git a/1.Hello World/1.2.1 Debug/.gitignore b/1.Hello World/1.2.1 Debug/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/1.Hello World/1.2.1 Debug/.gitignore @@ -0,0 +1 @@ +/target diff --git a/1.Hello World/1.2.1 Debug/Cargo.lock b/1.Hello World/1.2.1 Debug/Cargo.lock new file mode 100644 index 0000000..2967913 --- /dev/null +++ b/1.Hello World/1.2.1 Debug/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "Debug" +version = "0.1.0" diff --git a/1.Hello World/1.2.1 Debug/Cargo.toml b/1.Hello World/1.2.1 Debug/Cargo.toml new file mode 100644 index 0000000..ebb0042 --- /dev/null +++ b/1.Hello World/1.2.1 Debug/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "Debug" +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/1.Hello World/1.2.1 Debug/src/main.rs b/1.Hello World/1.2.1 Debug/src/main.rs new file mode 100644 index 0000000..caa7fc2 --- /dev/null +++ b/1.Hello World/1.2.1 Debug/src/main.rs @@ -0,0 +1,46 @@ +// 这个结构体不能被 `fmt::Display` 或 `fmt::Debug` 打印 +struct UnPrintable(i32); + +// `derive` 属性为当前的结构体自动实现了 `fmt::Debug` 的打印方法 +#[derive(Debug)] +struct DebugPrintable(i32); + +// derive宏为 `Structure` 自动实现了 `fmt::Debug`。 +// `Structure` 是一个结构体,这个结构体内部保存了一个 `i32` 类型的值。 +#[derive(Debug)] +struct Structure(i32); + +// 让 `Deep` 结构体嵌套 `Structure` 结构体,同时使用 `derive` 宏让 `Deep` 也是可打印的 +#[derive(Debug)] +struct Deep(Structure); + +#[derive(Debug)] +struct Person<'a> { + name: &'a str, + age: u8, +} + +fn main() { + // 使用 `fmt::Debug` 的标记 `{:?}` 和 `fmt::Dispaly` 的 `{}` 标记很像。 + println!("{:?} months in a year.", 12); + println!( + "{1:?} {0:?} is the {actor:?} name.", + "Slater", + "Christian", + actor = "actor's" + ); + + // `Structure` 结构体是可打印的 + println!("Now {:?} will print!", Structure(3)); + + // 有一个小问题是 `derive` 宏打印出来的内容格式是不可控的。 + // 如果我们只想打印出 `7` 应该怎么办? + println!("Now {:?} will print!", Deep(Structure(7))); + + let name = "Peter"; + let age = 27; + let peter = Person { name, age }; + + // 美化版的输出,带空格和缩进 + println!("{:#?}", peter); +} diff --git a/1.Hello World/1.2.2 Display/Cargo.lock b/1.Hello World/1.2.2 Display/Cargo.lock new file mode 100644 index 0000000..b9ae9bc --- /dev/null +++ b/1.Hello World/1.2.2 Display/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "Display" +version = "0.1.0" diff --git a/1.Hello World/1.2.2 Display/Cargo.toml b/1.Hello World/1.2.2 Display/Cargo.toml new file mode 100644 index 0000000..d50792b --- /dev/null +++ b/1.Hello World/1.2.2 Display/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "Display" +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/1.Hello World/1.2.2 Display/src/main.rs b/1.Hello World/1.2.2 Display/src/main.rs new file mode 100644 index 0000000..704cea8 --- /dev/null +++ b/1.Hello World/1.2.2 Display/src/main.rs @@ -0,0 +1,88 @@ +use std::fmt::write; + +fn example01() { + // 使用 `use` 关键字把 `fmt` 模块引入到当前作用域。 + use std::fmt; + + // 定义一个结构体然后实现 `fmt::Display` 特性。 + // 当前定义的结构体是一个 `Tuple (元组)`,改元组包含一个 `i32` 的值 + struct Structure(i32); + + // 想要通过 `{}` 标记格式化结构,必须手动实现 `fmt::Display` trait(特性) + impl fmt::Display for Structure { + // 这个特性要求必须实现 `fmt` 方法,该方法签名如下 + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + // 第一个参数是当前的结构体示例,第二个参数是输出的目标缓冲区(Buffer)。 + // 返回值是 `fmt::Result`,这个值表示格式化的结果。 + // `write!` 宏是和 `println!` 宏非常相似,只不过 `write!` 宏允许向指定向哪个缓冲区(Buffer)写出数据。 + write!(f, "{}", self.0) + } + } + + let demo = Structure(100); + + println!("struct display: {}", demo); +} + +fn example02() { + use std::fmt; // 导入 `fmt` + + // 该结构体内部有两个值,`derive(Debug)` 宏会自动实现 `fmt::Debug`方法。 + // 我们自己实现 `fmt::Display` 方法来对比两种实现的结果。 + #[derive(Debug)] + struct MinMax(i64, i64); + + // 手动为 `MinMax` 实现 `fmt::Display` 特性 + impl fmt::Display for MinMax { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // 使用 `self.number` 来引用对应位置的值,输出到结果缓冲区(Buffer)中。 + write!(f, "({}, {})", self.0, self.1) + } + } + + // 定义一个结构体,包含名字和值,用来对比不同结构下显示的内容 + #[derive(Debug)] + struct Point2D { + x: f64, + y: f64, + } + + // 类似的为 `Point2D` 结构体实现 `Display` + impl fmt::Display for Point2D { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + // 自定义输出格式,只显示 `x` 和 `y` + write!(f, "x: {}, y: {}", self.x, self.y) + } + } + + let minmax = MinMax(0, 14); + + println!("Compare structures:"); + println!("Display: {}", minmax); + println!("Debug: {:?}", minmax); + + let big_range = MinMax(-300, 300); + let small_range = MinMax(-3, 3); + + println!( + "The big range is {big} and the small is {small}", + small = small_range, + big = big_range + ); + + let point = Point2D { x: 3.3, y: 7.2 }; + + println!("Compare points:"); + println!("Display: {}", point); + println!("Debug: {:?}", point); + + // 下面这个例子会出现错误,因为我们只实现了 `Debug` 和 `Display` + // 但是 `{:b}` 需要额外实现 `fmt:Binary` 特性。 + // println!("What does Point2D look like in binary: {:b}?", point); +} + +fn main() { + example01(); + example02(); + println!("Hello, world!"); +} diff --git a/1.Hello World/1.2.2.1 Testcase List/Cargo.lock b/1.Hello World/1.2.2.1 Testcase List/Cargo.lock new file mode 100644 index 0000000..cbd770b --- /dev/null +++ b/1.Hello World/1.2.2.1 Testcase List/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "Testcase_List" +version = "0.1.0" diff --git a/1.Hello World/1.2.2.1 Testcase List/Cargo.toml b/1.Hello World/1.2.2.1 Testcase List/Cargo.toml new file mode 100644 index 0000000..5124d1b --- /dev/null +++ b/1.Hello World/1.2.2.1 Testcase List/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "Testcase_List" +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/1.Hello World/1.2.2.1 Testcase List/src/main.rs b/1.Hello World/1.2.2.1 Testcase List/src/main.rs new file mode 100644 index 0000000..5fc7507 --- /dev/null +++ b/1.Hello World/1.2.2.1 Testcase List/src/main.rs @@ -0,0 +1,32 @@ +use std::fmt; + +// 定义 `List` 结构体,内部包含一个 `Vec` +struct List(Vec); + +impl fmt::Display for List { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // 通过元组的索引,获取内部 `Vec` 的引用 + let vec = &self.0; + + // 不管有没有数据都要先打印一个开始括号 + // `?` 操作符表示如果 `write!` 宏出错了,则结束当前的函数,并把错误返回给调用方。 + write!(f, "[")?; + + // 使用 `Vec` 的 `iter()` 方法获取值引用的迭代器,再使用迭代器 `enumerate()` 返回一个带有计数器的迭代器 + for (count, value) in vec.iter().enumerate() { + // 除了第一个值以外的所有值都先在前面打印一个 `", "` 分隔符 + if count != 0 { + write!(f, ", ")?; + } + // 然后继续打印当前的数据 + write!(f, "{}", value)?; + } + // 最后补全结束的括号 + write!(f, "]") + } +} + +fn main() { + let v = List(vec![1, 2, 3]); + println!("{}", v); +} diff --git a/1.Hello World/1.2.3 Formatting/Cargo.lock b/1.Hello World/1.2.3 Formatting/Cargo.lock new file mode 100644 index 0000000..e98e00c --- /dev/null +++ b/1.Hello World/1.2.3 Formatting/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "Formatting" +version = "0.1.0" diff --git a/1.Hello World/1.2.3 Formatting/Cargo.toml b/1.Hello World/1.2.3 Formatting/Cargo.toml new file mode 100644 index 0000000..d5e9596 --- /dev/null +++ b/1.Hello World/1.2.3 Formatting/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "Formatting" +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/1.Hello World/1.2.3 Formatting/src/main.rs b/1.Hello World/1.2.3 Formatting/src/main.rs new file mode 100644 index 0000000..575c24f --- /dev/null +++ b/1.Hello World/1.2.3 Formatting/src/main.rs @@ -0,0 +1,118 @@ +use std::fmt::{self, Display, Formatter}; + +struct City { + name: &'static str, + // 纬度 + lat: f32, + // 经度 + lon: f32, +} + +impl Display for City { + // f 是一个缓冲区(Buffer),这个函数把格式化好的内容写到这个缓冲区中。 + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let lat_c = if self.lat >= 0.0 { 'N' } else { 'S' }; + let lon_c = if self.lon >= 0.0 { 'E' } else { 'W' }; + + // `write!` 和 `format!` 这两个宏非常相似, + // 只不过 `write!` 宏是向指定的缓冲区(第一个参数)中写数据,而`format!`是返回一个格式化好的字符串。 + write!( + f, + "{}: {:.3}°{} {:.3}°{}", + self.name, + self.lat.abs(), + lat_c, + self.lon.abs(), + lon_c + ) + } +} + +#[derive(Debug)] +struct Color { + red: u8, + green: u8, + blue: u8, +} + +// 实现 {} 特性 输出 RGB (x, x, x) 格式 +impl fmt::Display for Color { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "RGB ({}, {}, {})", self.red, self.green, self.blue) + } +} + +// 实现 {:x} 特性 输出 0xaabbcc 格式 +impl fmt::LowerHex for Color { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + // 使用 {:x} 格式把数字转成小写16进制文本 + let lower_hex = |x: u8| format!("{:x}", x); + // 合并输出三个独立颜色单元的文字并自动左侧补齐空格 + write!( + f, + "0x{:0>2}{:0>2}{:0>2}", + lower_hex(self.red), + lower_hex(self.green), + lower_hex(self.blue), + ) + } +} + +// 实现 {:X} 特性 输出 0xAABBCC 格式 +impl fmt::UpperHex for Color { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + // 使用 {:X} 格式把数字转成大写16进制文本 + let upper_hex = |x: u8| format!("{:X}", x); + // 同上 + write!( + f, + "0x{:0>2}{:0>2}{:0>2}", + upper_hex(self.red), + upper_hex(self.green), + upper_hex(self.blue), + ) + } +} + +fn main() { + for city in [ + City { + name: "Dublin", + lat: 53.347778, + lon: -6.259722, + }, + City { + name: "Oslo", + lat: 59.95, + lon: 10.75, + }, + City { + name: "Vancouver", + lat: 49.25, + lon: -123.1, + }, + ] { + println!("{}", city); + } + for color in [ + Color { + red: 128, + green: 255, + blue: 90, + }, + Color { + red: 0, + green: 3, + blue: 254, + }, + Color { + red: 0, + green: 0, + blue: 0, + }, + ] { + // 三种格式输出 `fmt::Display` `fmt::UpperHex` `fmt::LowerHex` + // 字符串,大写十六进制,小写十六进制 + println!("{0:?} {0:x} {0:X}", color); + } +} diff --git a/2.Primitives/2. Primitives/Cargo.lock b/2.Primitives/2. Primitives/Cargo.lock new file mode 100644 index 0000000..3524ea8 --- /dev/null +++ b/2.Primitives/2. Primitives/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "Primitives" +version = "0.1.0" diff --git a/2.Primitives/2. Primitives/Cargo.toml b/2.Primitives/2. Primitives/Cargo.toml new file mode 100644 index 0000000..129637e --- /dev/null +++ b/2.Primitives/2. Primitives/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "Primitives" +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/2.Primitives/2. Primitives/src/main.rs b/2.Primitives/2. Primitives/src/main.rs new file mode 100644 index 0000000..791be72 --- /dev/null +++ b/2.Primitives/2. Primitives/src/main.rs @@ -0,0 +1,25 @@ +fn main() { + // 明确声明变量类型 + let logical: bool = true; + + let a_float: f64 = 1.0; // 常规类型声明 + let an_integer = 5i32; // 后缀类型声明 + + // 默认的类型推断,整数型默认 i32,浮点型默认是 f64 + let default_float = 3.0; // `f64` + let default_integer = 7; // `i32` + + // 变量类型还可以根据代码的上下文自动推断 + let mut inferred_type = 12; // 这里的类型推断为 i64 是因为下面一行代码写的数值范围超过了 i32 类型的有效范围 + inferred_type = 4294967296i64; // 这里的数值超过了默认 i32 类型的取值范围,所以进行了类型扩容推断为 i64 + + // 所有的变量默认都是不可变类型,通过添加 mut 关键字可以让变量的值可修改 + let mut mutable = 12; // 可变的 `i32` 类型 + mutable = 21; + + // 错误!变量可变的前提条件是,变更的值类型不能变,这里报错就是因为尝试把 i32 类型的变量修改为 bool 类型 + mutable = true; + + // 变量可以通过 let 关键字重新定义(遮蔽:shadowing)来修改变量的类型。 + let mutable = true; +} diff --git a/2.Primitives/2.1 Literals and operators/Cargo.lock b/2.Primitives/2.1 Literals and operators/Cargo.lock new file mode 100644 index 0000000..9972d44 --- /dev/null +++ b/2.Primitives/2.1 Literals and operators/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "literals_and_operators" +version = "0.1.0" diff --git a/2.Primitives/2.1 Literals and operators/Cargo.toml b/2.Primitives/2.1 Literals and operators/Cargo.toml new file mode 100644 index 0000000..91abdb0 --- /dev/null +++ b/2.Primitives/2.1 Literals and operators/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "literals_and_operators" +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/2.Primitives/2.1 Literals and operators/src/main.rs b/2.Primitives/2.1 Literals and operators/src/main.rs new file mode 100644 index 0000000..ae9a377 --- /dev/null +++ b/2.Primitives/2.1 Literals and operators/src/main.rs @@ -0,0 +1,44 @@ +fn main() { + // 无符号整数加法 + println!("1 + 2 = {}", 1u32 + 2); + + // 有符号整数减法 + println!("1 - 2 = {}", 1i32 - 2); + // TODO ^ 修改 `1i32` 成 `1u32` 会编译失败,因为无符号不能表示负数 + + // 科学计数法 + println!("1e4 is {}, -2.5e-3 is {}", 1e4, -2.5e-3); + + // 逻辑表达式语法糖 + println!("true AND false is {}", true && false); + println!("true OR false is {}", true || false); + println!("NOT true is {}", !true); + + // 二进制操作 + + // 二进制 位与 + // 两个数字二进制的 相同位 都是 1 的时候,结果的对应位置上为 1 否则为 0 + println!("0011 AND 0101 is {:04b}", 0b0011u32 & 0b0101); + + // 二进制 位或 + // 两个数字二进制的 相同位 任意一个 1 的时候,结果的对应位置上为 1 否则为 0 + println!("0011 OR 0101 is {:04b}", 0b0011u32 | 0b0101); + + // 二进制 位异或 + // 两个数字二进制的 相同位 不相同的时候,结果的对应位置上为 1 否则为 0 + println!("0011 XOR 0101 is {:04b}", 0b0011u32 ^ 0b0101); + + // 二进制 左移 + // 数字的二进制位 整体向左移动指定位数 新增的位置补 0 + // 0b1 << 5 == 0b100000 == 32 + println!("1 << 5 is {}", 1u32 << 5); + + // 二进制 右移 + // 数字的二进制位 整体向右移动指定位数 移动过程中会丢弃末尾的二进制位 + // 0b10000000 >> 2 == 0b100000 == 32 == 0x20 + println!("0x80 >> 2 is 0x{:x}", 0x80u32 >> 2); + + // 可以通过在数字中插入 `_` 来让数字读起来更容易一些 + // 改特性不会影响数字本身,编译的过程中会自动去掉数字中的 `_` + println!("One million is written as {}", 1_000_000u32); +} diff --git a/2.Primitives/2.2 Tuples/Cargo.lock b/2.Primitives/2.2 Tuples/Cargo.lock new file mode 100644 index 0000000..78ce8a4 --- /dev/null +++ b/2.Primitives/2.2 Tuples/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "tuples" +version = "0.1.0" diff --git a/2.Primitives/2.2 Tuples/Cargo.toml b/2.Primitives/2.2 Tuples/Cargo.toml new file mode 100644 index 0000000..af7715e --- /dev/null +++ b/2.Primitives/2.2 Tuples/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "tuples" +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/2.Primitives/2.2 Tuples/src/main.rs b/2.Primitives/2.2 Tuples/src/main.rs new file mode 100644 index 0000000..dfa26f4 --- /dev/null +++ b/2.Primitives/2.2 Tuples/src/main.rs @@ -0,0 +1,66 @@ +// 元组可以当做函数参数,也可以当做函数返回值使用 +fn reverse(pair: (i32, bool)) -> (bool, i32) { + // `let` 关键字可以绑定元组的数据到变量上。 + let (int_param, bool_param) = pair; + + (bool_param, int_param) +} + +// 该结构用于下面代码示例 +#[derive(Debug)] +struct Matrix(f32, f32, f32, f32); + +impl std::fmt::Display for Matrix { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "( {} {} )\n( {} {} )", self.0, self.1, self.2, self.3) + } +} + +fn transpose(matrix: Matrix) -> Matrix { + Matrix(matrix.0, matrix.2, matrix.1, matrix.3) +} + +fn main() { + // 元组可以容纳任意数量的任意类型值 + let long_tuple = ( + 1u8, 2u16, 3u32, 4u64, -1i8, -2i16, -3i32, -4i64, 0.1f32, 0.2f64, 'a', true, + ); + + // 元组可以通过索引直接访问对应位置上的数据。 + println!("Long tuple first value: {}", long_tuple.0); + println!("Long tuple second value: {}", long_tuple.1); + + // 元组可以嵌套使用 + let tuple_of_tuples = ((1u8, 2u16, 2u32), (4u64, -1i8), -2i16); + + // 元组默认是可打印的 + println!("tuple of tuples: {:?}", tuple_of_tuples); + + // 但是元组的长度超过12个元素的话就不能打印了。 + // let too_long_tuple = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13); + // println!("Too long tuple: {:?}", too_long_tuple); + // TODO ^ 移除上面两行代码的注释看看编译错误 + + let pair = (1, true); + println!("Pair is {:?}", pair); + + println!("The reversed pair is {:?}", reverse(pair)); + + // 因为元组和表达式优先级使用了相同的语法。 + // 所以当想创建只有一个元素的元组类型,需要在元素后面加一个逗号来区分是元组还是表达式 + println!("One element tuple: {:?}", (5u32,)); + // 这个是优先级的表达式,返回一个整数 + println!("Just an integer: {:?}", (5u32)); + + // 元组可以使用 `let` 来进行结构批量赋值给变量。 + let tuple = (1, "hello", 4.5, true); + + let (a, b, c, d) = tuple; + println!("{:?}, {:?}, {:?}, {:?}", a, b, c, d); + + let matrix = Matrix(1.1, 1.2, 2.1, 2.2); + println!("{:?}", matrix); + + println!("Matrix:\n{}", matrix); + println!("Transpose:\n{}", transpose(matrix)); +} diff --git a/2.Primitives/2.3 Arrays and Slices/Cargo.lock b/2.Primitives/2.3 Arrays and Slices/Cargo.lock new file mode 100644 index 0000000..49182f3 --- /dev/null +++ b/2.Primitives/2.3 Arrays and Slices/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "arrays_and_slices" +version = "0.1.0" diff --git a/2.Primitives/2.3 Arrays and Slices/Cargo.toml b/2.Primitives/2.3 Arrays and Slices/Cargo.toml new file mode 100644 index 0000000..3c549a9 --- /dev/null +++ b/2.Primitives/2.3 Arrays and Slices/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "arrays_and_slices" +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/2.Primitives/2.3 Arrays and Slices/src/main.rs b/2.Primitives/2.3 Arrays and Slices/src/main.rs new file mode 100644 index 0000000..43e1f75 --- /dev/null +++ b/2.Primitives/2.3 Arrays and Slices/src/main.rs @@ -0,0 +1,58 @@ +use std::mem; + +// 该函数借用数组切片(slice) +fn analyze_slice(slice: &[i32]) { + println!("First element of the slice: {}", slice[0]); + println!("The slice has {} elements", slice.len()); +} + +fn main() { + // 固定长度和类型的数组,这里可以不声明类型和长度,编译器会自动推断 + let xs: [i32; 5] = [1, 2, 3, 4, 5]; + + // 数组类型是 i32 长度是 500,所有 500 个元素初始化为 0. + let ys: [i32; 500] = [0; 500]; + + // 数组的索引从 0 开始 + println!("First element of the array: {}", xs[0]); + println!("Second element of the array: {}", xs[1]); + + // `len()` 方法会返回数组的长度。 + println!("Number of elements in array: {}", xs.len()); + + // 因为数组的类型和长度是固定的,编译的时候会直接把数组放在栈上。 + println!("Array occupies {} bytes", mem::size_of_val(&xs)); + + // 数组可以自动创建一个对于该数组的切片(slice) + println!("Borrow the whole array as a slice."); + analyze_slice(&xs); + + // 切片(slice)可以通过指定一个区间 [起始索引..结束索引] 来借用数组的一部分数据 + // `起始索引` 是切片的第一个元素的位置 + // `结束索引` 是切片的最后一个元素的位置 + 1 的位置 + println!("Borrow a section of the array as a slice."); + analyze_slice(&ys[1..4]); + + // 创建一个空的切片 `&[]`: + let empty_array: [u32; 0] = []; + assert_eq!(&empty_array, &[]); // 对空数组创建一个切片引用,实际上这个等于下面的代码。 + assert_eq!(&empty_array, &[][..]); // 对空数组创建切片引用,引用范围为整个数组。 + + // 数组还可以通过 `.get()` 方法安全的访问,该方法会返回一个 `Option` + // 可以通过 `.expect()` 方法来输出一个明确的错误。 + for i in 0..xs.len() + 1 { + // Oops, one element too far! + match xs.get(i) { + Some(xval) => println!("{}: {}", i, xval), + None => println!("Slow down! {} is too far!", i), + } + } + + // 因为数组类型是明确长度和类型的,所以在编译阶段会进行数组的访问越界检查, + // 一旦发现越界就会报出错误,无法通过编译。 + //println!("{}", xs[5]); + + // 切片类型在编译阶段是不能知道长度的, + // 所以切片会在运行的过程中检查访问的索引是否越界,一旦越界就会产生 `panic!` 错误。 + //println!("{}", xs[..][5]); +} diff --git a/3.Custom Types/3.1 structures/Cargo.lock b/3.Custom Types/3.1 structures/Cargo.lock new file mode 100644 index 0000000..c8bd006 --- /dev/null +++ b/3.Custom Types/3.1 structures/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "structures" +version = "0.1.0" diff --git a/3.Custom Types/3.1 structures/Cargo.toml b/3.Custom Types/3.1 structures/Cargo.toml new file mode 100644 index 0000000..20a46bf --- /dev/null +++ b/3.Custom Types/3.1 structures/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "structures" +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/3.Custom Types/3.1 structures/src/main.rs b/3.Custom Types/3.1 structures/src/main.rs new file mode 100644 index 0000000..7a8143b --- /dev/null +++ b/3.Custom Types/3.1 structures/src/main.rs @@ -0,0 +1,117 @@ +// 禁用未使用的代码警告 +#![allow(dead_code)] + +#[derive(Debug)] +struct Person { + name: String, + age: u8, +} + +// 单位结构 类似 单位元组(Unit Tuple) +// 虽然和单位元组类似,但是每一个 struct 都是独一无二的。 +struct Unit; + +// 元组结构 +struct Pair(i32, f32); + +// 两个字段的结构 +#[derive(Debug)] +struct Point { + x: f32, + y: f32, +} + +// 结构体可以嵌套使用 +#[derive(Debug)] +struct Rectangle { + // 长方形可以使用 top、left 和 bottom、right 两个点来表示 + top_left: Point, + bottom_right: Point, +} + +impl Rectangle { + fn rect_area(&self) -> f64 { + // 解构两个坐标点 + let Rectangle { + top_left, + bottom_right, + } = self; + // 计算宽高 + let width = bottom_right.x - top_left.x; + let height = bottom_right.y - top_left.y; + // 计算面积 + width as f64 * height as f64 + } + + fn square(top_left: &Point, size: f32) -> Rectangle { + Rectangle { + // 复制 Point 结构体 + top_left: Point { ..*top_left }, + // 按照尺寸对左上角的坐标进行扩大 + bottom_right: Point { + x: top_left.x + size, + y: top_left.y + size, + }, + } + } +} + +fn main() { + let name = String::from("Peter"); + let age = 27; + // 如果变量和结构体的字段名正好相同,则可以省略字段名 + let peter = Person { name, age }; + + // 使用 fmt::Debug 来打印结构体 + println!("{:?}", peter); + + // 实例化 `Point` 结构体 + let point: Point = Point { x: 10.3, y: 0.4 }; + + // 通过字段访问结构体 + println!("point coordinates: ({}, {})", point.x, point.y); + + // 使用一个已经存在的结构体的内容来补充剩余的字段,创建一个新的结构体。 + let bottom_right = Point { x: 5.2, ..point }; + + // `bottom_right.y` will be the same as `point.y` because we used that field + // from `point` + + // `bottom_right.y` 和 `point.y` 的值相同, + // 因为 `button_right.y` 的值是从 `point` 上复制过来的 + println!("second point: ({}, {})", bottom_right.x, bottom_right.y); + + // `let` 也可以对结构体进行结构赋值 + let Point { + x: left_edge, + y: top_edge, + } = point; + + let _rectangle = Rectangle { + // 实例化结构体也是一个表达式,该表达式返回新的结构体实例 + top_left: Point { + x: left_edge, + y: top_edge, + }, + bottom_right: bottom_right, + }; + + // 实例化一个 `Unit` 结构 + let _unit = Unit; + + // 实例化一个 元组结构体 + let pair = Pair(1, 0.1); + + // 访问元组结构体和元组本身的访问行为相同 + println!("pair contains {:?} and {:?}", pair.0, pair.1); + + // 解构元组结构体同样和解构元组相同 + let Pair(integer, decimal) = pair; + + println!("pair contains {:?} and {:?}", integer, decimal); + + let square = Rectangle::square(&point, 100f32); + + println!("square: \n{:#?}", square); + println!("square area: {}", square.rect_area()); +} diff --git a/3.Custom Types/3.2 Eumns/Cargo.lock b/3.Custom Types/3.2 Eumns/Cargo.lock new file mode 100644 index 0000000..c514693 --- /dev/null +++ b/3.Custom Types/3.2 Eumns/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "enums" +version = "0.1.0" diff --git a/3.Custom Types/3.2 Eumns/Cargo.toml b/3.Custom Types/3.2 Eumns/Cargo.toml new file mode 100644 index 0000000..7b703c4 --- /dev/null +++ b/3.Custom Types/3.2 Eumns/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "enums" +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/3.Custom Types/3.2 Eumns/src/main.rs b/3.Custom Types/3.2 Eumns/src/main.rs new file mode 100644 index 0000000..64e5fc9 --- /dev/null +++ b/3.Custom Types/3.2 Eumns/src/main.rs @@ -0,0 +1,73 @@ +fn example01() { + // 使用枚举类型对web事件进行分类。 + // 枚举的每一项都是独一无二的,枚举的每一项都可以包含任意数据 + // 但是枚举的每一项都属于该枚举类型 + // `PageLoad != PageUnload` 和 `KeyPress(char) != Paste(String)` 都是成立的 + + enum WebEvent { + // 枚举的每一项都是一个独立的 + PageLoad, + PageUnload, + // 使用类似元组结构来保存数据 + KeyPress(char), + Paste(String), + // 或者使用结构体保存数据 + Click { x: i64, y: i64 }, + } + + // 该函数接收 `WebEvent` 枚举,也就是说可以接受该枚举下定义的任意一项数据 + fn inspect(event: WebEvent) { + match event { + WebEvent::PageLoad => println!("page loaded"), + WebEvent::PageUnload => println!("page unloaded"), + // Destructure `c` from inside the `enum` variant. + WebEvent::KeyPress(c) => println!("pressed '{}'.", c), + WebEvent::Paste(s) => println!("pasted \"{}\".", s), + // Destructure `Click` into `x` and `y`. + WebEvent::Click { x, y } => { + println!("clicked at x={}, y={}.", x, y); + } + } + } + + let pressed = WebEvent::KeyPress('x'); + // `to_owned()` 通过克隆 `&str` 引用的数据 创建一新的 `String` 类型 + let pasted = WebEvent::Paste("my text".to_owned()); + let click = WebEvent::Click { x: 20, y: 80 }; + let load = WebEvent::PageLoad; + let unload = WebEvent::PageUnload; + + inspect(pressed); + inspect(pasted); + inspect(click); + inspect(load); + inspect(unload); +} + +fn example02() { + enum VeryVerboseEnumOfThingsToDoWithNumbers { + Add, + Subtract, + } + + impl VeryVerboseEnumOfThingsToDoWithNumbers { + fn run(&self, x: i32, y: i32) -> i32 { + match self { + // 这里的 Self 就是一个类型别名,等于当前 impl 的类型 + Self::Add => x + y, + Self::Subtract => x - y, + } + } + } + + // 创建一个类型别名,类型别名并没有定义新的类型,只是用作缩短类型声明 + type Operations = VeryVerboseEnumOfThingsToDoWithNumbers; + + // 类型别名可以有效的缩短类型声明 + let x = Operations::Add; +} + +fn main() { + example01(); + example02(); +} diff --git a/3.Custom Types/3.2.1 use/Cargo.lock b/3.Custom Types/3.2.1 use/Cargo.lock new file mode 100644 index 0000000..7bd3fab --- /dev/null +++ b/3.Custom Types/3.2.1 use/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "uses" +version = "0.1.0" diff --git a/3.Custom Types/3.2.1 use/Cargo.toml b/3.Custom Types/3.2.1 use/Cargo.toml new file mode 100644 index 0000000..24f0baa --- /dev/null +++ b/3.Custom Types/3.2.1 use/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "uses" +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/3.Custom Types/3.2.1 use/src/main.rs b/3.Custom Types/3.2.1 use/src/main.rs new file mode 100644 index 0000000..6d55c87 --- /dev/null +++ b/3.Custom Types/3.2.1 use/src/main.rs @@ -0,0 +1,38 @@ +// 禁用未使用的代码警告 +#![allow(dead_code)] + +enum Status { + Rich, + Poor, +} + +enum Work { + Civilian, + Soldier, +} + +fn main() { + // crate 关键字等于当前的根作用域。 + // 使用 `use` 关键字把指定类型引用到当前作用域, + // 这样每次使用导入的类型的时候就不需要明确指定完整的路径了 + use crate::Status::{Poor, Rich}; + // 可以使用通配符 `*` 把对应作用域下的所有导出内容都导入到当前作用域下。 + use crate::Work::*; + + // 等于 Status::Poor + let status = Poor; + // 等于 Work::Civilian + let work = Civilian; + + match status { + // 因为前面明确的使用 `use` 关键字把 `Status` 导入到了当前作用域,所以这里不需要写 `Status::` + Rich => println!("The rich have lots of money!"), + Poor => println!("The poor have no money..."), + } + + match work { + // 同上 + Civilian => println!("Civilians work!"), + Soldier => println!("Soldiers fight!"), + } +} diff --git a/3.Custom Types/3.2.2 C-like/Cargo.lock b/3.Custom Types/3.2.2 C-like/Cargo.lock new file mode 100644 index 0000000..6e72b87 --- /dev/null +++ b/3.Custom Types/3.2.2 C-like/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "c-like" +version = "0.1.0" diff --git a/3.Custom Types/3.2.2 C-like/Cargo.toml b/3.Custom Types/3.2.2 C-like/Cargo.toml new file mode 100644 index 0000000..bf61489 --- /dev/null +++ b/3.Custom Types/3.2.2 C-like/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "c-like" +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/3.Custom Types/3.2.2 C-like/src/main.rs b/3.Custom Types/3.2.2 C-like/src/main.rs new file mode 100644 index 0000000..d31b73b --- /dev/null +++ b/3.Custom Types/3.2.2 C-like/src/main.rs @@ -0,0 +1,25 @@ +// 禁用未使用的代码警告 +#![allow(dead_code)] + +// 当不指明任意类型的时候,枚举项默认从 0 开始 +enum Number { + Zero, + One, + Two, +} + +// 明确指明枚举项的值 +enum Color { + Red = 0xff0000, + Green = 0x00ff00, + Blue = 0x0000ff, +} + +fn main() { + // 枚举可以强制转换成数字 + println!("zero is {}", Number::Zero as i32); + println!("one is {}", Number::One as i32); + + println!("roses are #{:06x}", Color::Red as i32); + println!("violets are #{:06x}", Color::Blue as i32); +} diff --git a/3.Custom Types/3.2.3 Testcase linked-list/Cargo.lock b/3.Custom Types/3.2.3 Testcase linked-list/Cargo.lock new file mode 100644 index 0000000..6cdc145 --- /dev/null +++ b/3.Custom Types/3.2.3 Testcase linked-list/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "testcase_linked_list" +version = "0.1.0" diff --git a/3.Custom Types/3.2.3 Testcase linked-list/Cargo.toml b/3.Custom Types/3.2.3 Testcase linked-list/Cargo.toml new file mode 100644 index 0000000..2667046 --- /dev/null +++ b/3.Custom Types/3.2.3 Testcase linked-list/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "testcase_linked_list" +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/3.Custom Types/3.2.3 Testcase linked-list/src/main.rs b/3.Custom Types/3.2.3 Testcase linked-list/src/main.rs new file mode 100644 index 0000000..b39573f --- /dev/null +++ b/3.Custom Types/3.2.3 Testcase linked-list/src/main.rs @@ -0,0 +1,61 @@ +// 把 List 枚举定义的所有项都导入到全局 +use crate::List::*; +enum List { + // Cons 定义为元组类型,第一个位是值,第二位是下一个值的指针 + Cons(u32, Box), + // Nil 表示链表结束 没有下一项了 + Nil, +} + +impl List { + fn new() -> List { + Nil + } + + fn prepend(self, elem: u32) -> List { + // 生成一个新的 Cons 把当前的链表放到新的 Cons 中 + Cons(elem, Box::new(self)) + } + + // 计算链表长度 + fn len(&self) -> usize { + // 因为 Rust 会自动进行解引用操作 所以这里可以直接使用 self 而不是使用 *self + match self { + // 递归计算所有链表节点 + Cons(_, next) => 1 + next.len(), + // 末尾节点没有值直接返回 0 + Nil => 0, + } + } + + // 格式化链表成字符串 + fn stringify(&self) -> String { + // *号解引用会转移变量所有权,但是因为参数上声明是个引用 + // 所以这里会报错,不能把值从引用中转移出来 + // 解决方案就是在声明语句前加上 ref 关键字来表示变量是引用形式使用的 + + match *self { + // 这里使用 ref 关键字来声明变量是一个引用,而不是转移变量的所有权 + // head 不需要添加 ref 关键字使用为 head 是一个 i32 的基础值,直接保存在栈上 + // 而 Box 的值是保存在堆上的。 + Cons(head, ref tail) => { + format!("{}, {}", head, tail.stringify()) + } + Nil => format!("Nil"), + } + } +} + +fn main() { + // 创建一个空链表 + let mut list = List::new(); + + // 从前面开始添加一些节点 + list = list.prepend(1); + list = list.prepend(2); + list = list.prepend(3); + + // 查看链表的状态 + println!("linked list has length: {}", list.len()); + println!("{}", list.stringify()); +} diff --git a/3.Custom Types/3.3 constants/Cargo.lock b/3.Custom Types/3.3 constants/Cargo.lock new file mode 100644 index 0000000..1950961 --- /dev/null +++ b/3.Custom Types/3.3 constants/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "constants" +version = "0.1.0" diff --git a/3.Custom Types/3.3 constants/Cargo.toml b/3.Custom Types/3.3 constants/Cargo.toml new file mode 100644 index 0000000..06d8173 --- /dev/null +++ b/3.Custom Types/3.3 constants/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "constants" +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/3.Custom Types/3.3 constants/src/main.rs b/3.Custom Types/3.3 constants/src/main.rs new file mode 100644 index 0000000..d187f98 --- /dev/null +++ b/3.Custom Types/3.3 constants/src/main.rs @@ -0,0 +1,21 @@ +// 全局定义常量 +static LANGUAGE: &str = "Rust"; // 可以被修改的常量,修改常量是不安全的 +const THRESHOLD: i32 = 10; // 不能修改的常量 + +fn is_big(n: i32) -> bool { + // 全局的常量可以在任意位置访问 + n > THRESHOLD +} + +fn main() { + let n = 16; + + // 在主线程访问常量 + println!("This is {}", LANGUAGE); + println!("The threshold is {}", THRESHOLD); + println!("{} is {}", n, if is_big(n) { "big" } else { "small" }); + + // 报错! 不能修改常量的值 + THRESHOLD = 5; + // FIXME ^ 注释掉这行代码 +} diff --git a/4.Variable Bindings/4. Varable Bindings/Cargo.lock b/4.Variable Bindings/4. Varable Bindings/Cargo.lock new file mode 100644 index 0000000..26854e3 --- /dev/null +++ b/4.Variable Bindings/4. Varable Bindings/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "veriable_bindings" +version = "0.1.0" diff --git a/4.Variable Bindings/4. Varable Bindings/Cargo.toml b/4.Variable Bindings/4. Varable Bindings/Cargo.toml new file mode 100644 index 0000000..12fb72a --- /dev/null +++ b/4.Variable Bindings/4. Varable Bindings/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "veriable_bindings" +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/4.Variable Bindings/4. Varable Bindings/src/main.rs b/4.Variable Bindings/4. Varable Bindings/src/main.rs new file mode 100644 index 0000000..47c3da7 --- /dev/null +++ b/4.Variable Bindings/4. Varable Bindings/src/main.rs @@ -0,0 +1,19 @@ +fn main() { + let an_integer = 1u32; + let a_boolean = true; + let unit = (); + + // 储存在栈上的基础值,会直接复制 + let copied_integer = an_integer; + + println!("An integer: {:?}", copied_integer); + println!("A boolean: {:?}", a_boolean); + println!("Meet the unit value: {:?}", unit); + + // 没有被使用的变量编译器会发出警告,可以通过在变量前面添加一个下划线来禁用编译器警告 + let _unused_variable = 3u32; + + let noisy_unused_variable = 2u32; + // FIXME ^ 在变量名前面添加下划线来禁用警告 + // PS:网页的在线测试看不到警告信息 +} diff --git a/4.Variable Bindings/4.1 Mutability/Cargo.lock b/4.Variable Bindings/4.1 Mutability/Cargo.lock new file mode 100644 index 0000000..18e6ed9 --- /dev/null +++ b/4.Variable Bindings/4.1 Mutability/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "mutability" +version = "0.1.0" diff --git a/4.Variable Bindings/4.1 Mutability/Cargo.toml b/4.Variable Bindings/4.1 Mutability/Cargo.toml new file mode 100644 index 0000000..b0efabb --- /dev/null +++ b/4.Variable Bindings/4.1 Mutability/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "mutability" +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/4.Variable Bindings/4.1 Mutability/src/main.rs b/4.Variable Bindings/4.1 Mutability/src/main.rs new file mode 100644 index 0000000..4525428 --- /dev/null +++ b/4.Variable Bindings/4.1 Mutability/src/main.rs @@ -0,0 +1,14 @@ +fn main() { + let _immutable_binding = 1; + let mut mutable_binding = 1; + + println!("Before mutation: {}", mutable_binding); + + // 可变声明,所以可以修改 + mutable_binding += 1; + + println!("After mutation: {}", mutable_binding); + + // 报错! 不可变声明不能修改内容 + _immutable_binding += 1; +} diff --git a/4.Variable Bindings/4.2 Scope and Shadowing/Cargo.lock b/4.Variable Bindings/4.2 Scope and Shadowing/Cargo.lock new file mode 100644 index 0000000..8beeb85 --- /dev/null +++ b/4.Variable Bindings/4.2 Scope and Shadowing/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "scope_and_shadowing" +version = "0.1.0" diff --git a/4.Variable Bindings/4.2 Scope and Shadowing/Cargo.toml b/4.Variable Bindings/4.2 Scope and Shadowing/Cargo.toml new file mode 100644 index 0000000..ed1465d --- /dev/null +++ b/4.Variable Bindings/4.2 Scope and Shadowing/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "scope_and_shadowing" +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/4.Variable Bindings/4.2 Scope and Shadowing/src/main.rs b/4.Variable Bindings/4.2 Scope and Shadowing/src/main.rs new file mode 100644 index 0000000..02b564f --- /dev/null +++ b/4.Variable Bindings/4.2 Scope and Shadowing/src/main.rs @@ -0,0 +1,45 @@ +fn example01() { + // 在当前函数作用域中 绑定 1 到变量 long_lived_binding + let long_lived_binding = 1; + + // 这个是一个内部嵌套的小范围作用域 + { + // 这个变量声明只在当前的小范围内生效 + let short_lived_binding = 2; + + println!("inner short: {}", short_lived_binding); + } + // 嵌套的作用域结束,该作用域内的全部变量都会被回收 + + // 报错! `short_lived_binding` 这个变量已经被回收了 + println!("outer short: {}", short_lived_binding); + // FIXME ^ 注释这行代码 + + println!("outer long: {}", long_lived_binding); +} + +fn example02() { + let shadowed_binding = 1; + + { + // 这里使用的变量值是外面的函数作用域的值 + println!("before being shadowed: {}", shadowed_binding); + + // 这里重新定义了一个同名的变量,进行了变量的遮蔽(shadowing) + let shadowed_binding = "abc"; + + println!("shadowed in inner block: {}", shadowed_binding); + } + // 小作用域已经结束,这里的变量使用的依旧是函数作用域的值 + println!("outside inner block: {}", shadowed_binding); + + // 这里对上面定义的变量重新定义,遮蔽(shadowing)了前面的变量声明 + let shadowed_binding = 2; + println!("shadowed in outer block: {}", shadowed_binding); +} + +fn main() { + example01(); + example02(); + println!("Hello, world!"); +} diff --git a/4.Variable Bindings/4.3 Declare first/Cargo.lock b/4.Variable Bindings/4.3 Declare first/Cargo.lock new file mode 100644 index 0000000..8387dd7 --- /dev/null +++ b/4.Variable Bindings/4.3 Declare first/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "declare_first" +version = "0.1.0" diff --git a/4.Variable Bindings/4.3 Declare first/Cargo.toml b/4.Variable Bindings/4.3 Declare first/Cargo.toml new file mode 100644 index 0000000..0f6e0ae --- /dev/null +++ b/4.Variable Bindings/4.3 Declare first/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "declare_first" +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/4.Variable Bindings/4.3 Declare first/src/main.rs b/4.Variable Bindings/4.3 Declare first/src/main.rs new file mode 100644 index 0000000..7e15bad --- /dev/null +++ b/4.Variable Bindings/4.3 Declare first/src/main.rs @@ -0,0 +1,30 @@ +//! +//! 变量的定义和初始化通常是同时进行的。 +//! Rust 允许把两个操作分开,先定义,后初始化 +//! 使用变量的前提条件是变量必须被初始化了以后才能使用,否则在编译阶段就会报错 +//! +//! 因为使用未初始化的变量通常会导致无法预知的行为,所以不允许使用未初始化的变量 +//! +fn main() { + // 声明一个变量 但不对其进行初始化 + let a_binding; + + { + let x = 2; + + // 初始化变量值 + a_binding = x * x; + } + + println!("a binding: {}", a_binding); + + let another_binding; + + // 报错! 使用了未初始化的变量 + println!("another binding: {}", another_binding); + // FIXME ^ 注释这行代码 + + another_binding = 1; + + println!("another binding: {}", another_binding); +} diff --git a/4.Variable Bindings/4.4 Freezing/Cargo.lock b/4.Variable Bindings/4.4 Freezing/Cargo.lock new file mode 100644 index 0000000..f082c07 --- /dev/null +++ b/4.Variable Bindings/4.4 Freezing/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "freezing" +version = "0.1.0" diff --git a/4.Variable Bindings/4.4 Freezing/Cargo.toml b/4.Variable Bindings/4.4 Freezing/Cargo.toml new file mode 100644 index 0000000..a4aa73e --- /dev/null +++ b/4.Variable Bindings/4.4 Freezing/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "freezing" +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/4.Variable Bindings/4.4 Freezing/src/main.rs b/4.Variable Bindings/4.4 Freezing/src/main.rs new file mode 100644 index 0000000..2dbb732 --- /dev/null +++ b/4.Variable Bindings/4.4 Freezing/src/main.rs @@ -0,0 +1,22 @@ +//! +//! 可以通过使用子作用域遮蔽(shadowing)父作用域的同名可变变量成不可变变量 +//! 这样在这个子作用域内的同名变量就会是 冻结(Freezing)状态 +//! 直到子作用域结束 +//! +fn main() { + let mut _mutable_integer = 7i32; + + { + // 使用不可变遮蔽父作用域的同名变量 + let _mutable_integer = _mutable_integer; + + // 报错! 当前作用域中 `_mutable_integer` 变量是不可变的 + _mutable_integer = 50; + // FIXME ^ 注释这行代码 + + // `_mutable_integer` 变量作用域结束 + } + + // `_mutable_integer` 这里可以修改,因为父作用域的定义是可变的 + _mutable_integer = 3; +} diff --git a/5.Types/5.1 casting/Cargo.lock b/5.Types/5.1 casting/Cargo.lock new file mode 100644 index 0000000..b1e3d5c --- /dev/null +++ b/5.Types/5.1 casting/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "casting" +version = "0.1.0" diff --git a/5.Types/5.1 casting/Cargo.toml b/5.Types/5.1 casting/Cargo.toml new file mode 100644 index 0000000..e90b0d3 --- /dev/null +++ b/5.Types/5.1 casting/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "casting" +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/5.Types/5.1 casting/src/main.rs b/5.Types/5.1 casting/src/main.rs new file mode 100644 index 0000000..67b19ef --- /dev/null +++ b/5.Types/5.1 casting/src/main.rs @@ -0,0 +1,88 @@ +//! +//! Rust 没有为基础类型提供隐式的类型强制转换 +//! 但是可以使用 `as` 关键字对基础类型进行明确的强制类型转换 +//! +//! 数字类型的强制转换规则遵循C的转换规则,除非某些在C中未定义的行为, +//! Rust 明确的规定了所有整数之间的转换规则。 +//! +// Suppress all warnings from casts which overflow. +#![allow(overflowing_literals)] + +fn main() { + let decimal = 65.4321_f32; + + // 错误! 没有从浮点数到整数的隐式转换规则。 + let integer: u8 = decimal; + // FIXME ^ 注释这行代码 + + // 手动明确指定转换规则 + let integer = decimal as u8; // f32 强制转换到 u8 + let character = integer as char; // u8 强制转换到 char + + // 错误! 有几个转换限制,f32 不能直接转换为 char + let character = decimal as char; + // FIXME ^ 注释这行代码 + + println!("Casting: {} -> {} -> {}", decimal, integer, character); + + // 当任意类型转换到无符号类型的时候,源类型的数据会被加或减到目标类型的最大容量值, + // 直到数据能够存放到目标类型内为止 + + // 如果数字能直接被目标类型存放则直接转换 + println!("1000 as a u16 is: {}", 1000 as u16); + + // 底层会保留前八个比特位 (LSB) + // 剩余的其他位 (MSB) 会被截断 + // 1000 - 256 - 256 - 256 = 232 + println!("1000 as a u8 is : {}", 1000 as u8); + // -1 + 256 = 255 + println!(" -1 as a u8 is : {}", (-1i8) as u8); + + // 正整数的情况下和取模的计算相同 + println!("1000 mod 256 is : {}", 1000 % 256); + + // 转换为有符号整数的时候,除了源数据的第一位以外,其他的位会自动转到对应目标位上 + // 如果原始数据的最高位为1的话,转换的结果就是负数 + + // 目标类型能放下直接转换 + println!(" 128 as a i16 is: {}", 128 as i16); + + // 如果原始值正好是目标的正负边界值,则会直接使用这个值 + // 128 的二进制是 0b1000_0000 因为负整数的二进制表示形式就是 + // 最高位比特位为 1 表示为负数 剩余的位数以补码形式展示 + // 所以这里的结果就是 -128 + println!(" 128 as a i8 is : {}", 128 as i8); + + // 重复上面的例子 有符号转无符号 + // 1000 as u8 -> 232 + println!("1000 as a u8 is : {}", 1000 as u8); + // 232的二进制是 0b1110_1000 直接可以放到 i8 的类型中 + // 计算过程 0b1000_0000 - 0b110_1000 = 24 + // 因为最高位是 1 所以剩余的位按照补码形式显示 最终的结果是 -24 + println!(" 232 as a i8 is : {}", 232 as i8); + + // 从 Rust 1.45 以后 `as` 关键字在转换`浮点数`到`整数`的时候表现为 *饱和转换* + // 如果浮点数超过了目标类型的最大边界,则转换为目标的最大边界, + // 如果浮点数小于目标类型的最小边界,则转换为目标的最小边界。 + + // 300.0 转换为 u8 是 255 + println!(" 300.0 as u8 is : {}", 300.0_f32 as u8); + // -100.0 转换为 u8 是 0 + println!("-100.0 as u8 is : {}", -100.0_f32 as u8); + // nan 转换为 u8 是 0 + println!(" nan as u8 is : {}", f32::NAN as u8); + + // 上面这些转换会有一些运行时的性能损失,可以通过使用不安全的方法来避免, + // 但是这些方法可能会溢出并返回不安全的值,应该小心使用这些方法。 + unsafe { + // 300.0 转换为 u8 是 44 + println!(" 300.0 as u8 is : {}", 300.0_f32.to_int_unchecked::()); + // -100.0 转换为 u8 是 156 + println!( + "-100.0 as u8 is : {}", + (-100.0_f32).to_int_unchecked::() + ); + // nan 转换为 u8 是 0 + println!(" nan as u8 is : {}", f32::NAN.to_int_unchecked::()); + } +} diff --git a/5.Types/5.2 Literals/Cargo.lock b/5.Types/5.2 Literals/Cargo.lock new file mode 100644 index 0000000..7dba0e7 --- /dev/null +++ b/5.Types/5.2 Literals/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "literals" +version = "0.1.0" diff --git a/5.Types/5.2 Literals/Cargo.toml b/5.Types/5.2 Literals/Cargo.toml new file mode 100644 index 0000000..0f9ae2a --- /dev/null +++ b/5.Types/5.2 Literals/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "literals" +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/5.Types/5.2 Literals/src/main.rs b/5.Types/5.2 Literals/src/main.rs new file mode 100644 index 0000000..c43f7a6 --- /dev/null +++ b/5.Types/5.2 Literals/src/main.rs @@ -0,0 +1,25 @@ +//! +//! 字面量数字可以通过添加类型后缀来指定变量类型, +//! 比如说 字面量数字 42 是 i32 类型,可以写成 42i32 来声明 42 是 i32 类型的 +//! +//! 如果数字字面量没有明确指明类型,编译器会自动根据使用的地方自动推断合适的类型, +//! 如果没有地方使用的话,整数型会默认推断为 i32,浮点数会自动推断为 f64 +//! + +fn main() { + // 带后缀的声明 + let x = 1u8; + let y = 2u32; + let z = 3f32; + + // 没有后缀的类型声明,编译器会根据变量的使用自动推断合适的类型,如果无法推断则使用默认类型 + let i = 1; // 整数默认类型 i32 + let f = 1.0; // 浮点数默认类型 f64 + + // `size_of_val()` 方法返回变量使用了多少个字节(1个字节使用8个比特位) + println!("size of `x` in bytes: {}", std::mem::size_of_val(&x)); + println!("size of `y` in bytes: {}", std::mem::size_of_val(&y)); + println!("size of `z` in bytes: {}", std::mem::size_of_val(&z)); + println!("size of `i` in bytes: {}", std::mem::size_of_val(&i)); + println!("size of `f` in bytes: {}", std::mem::size_of_val(&f)); +} diff --git a/5.Types/5.3 Inference/Cargo.lock b/5.Types/5.3 Inference/Cargo.lock new file mode 100644 index 0000000..1823cb4 --- /dev/null +++ b/5.Types/5.3 Inference/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "inference" +version = "0.1.0" diff --git a/5.Types/5.3 Inference/Cargo.toml b/5.Types/5.3 Inference/Cargo.toml new file mode 100644 index 0000000..067e19c --- /dev/null +++ b/5.Types/5.3 Inference/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "inference" +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/5.Types/5.3 Inference/src/main.rs b/5.Types/5.3 Inference/src/main.rs new file mode 100644 index 0000000..55525e1 --- /dev/null +++ b/5.Types/5.3 Inference/src/main.rs @@ -0,0 +1,23 @@ +//! +//! Rust的智能类型推断引擎,可以根据代码的上下文来自动推断所需要的类型定义 +//! 所以很多时候不需要手动声明类型,让推断引擎自动推断出类型 +//! + +fn main() { + // 手动声明类型 + let elem = 5u8; + + // 创建一个 Vec 的容器 + let mut vec = Vec::new(); + // 在这里编译器并不知道vec是什么类型的,所以编译器先假定类型是 Vec<_> 类型 + // At this point the compiler doesn't know the exact type of `vec`, it + // just knows that it's a vector of something (`Vec<_>`). + + // Insert `elem` in the vector. + // 插入一下明确的类型 + vec.push(elem); + // 编译器在这里发现我们插入了一个明确的类型,编译器就会把 vec 推断成 Vec 类型 + // TODO ^ 尝试注释上面这行代码看看 vec 的类型是什么 + + println!("{:?}", vec); +} diff --git a/5.Types/5.4 Aliasing/Cargo.lock b/5.Types/5.4 Aliasing/Cargo.lock new file mode 100644 index 0000000..213ce09 --- /dev/null +++ b/5.Types/5.4 Aliasing/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aliasing" +version = "0.1.0" diff --git a/5.Types/5.4 Aliasing/Cargo.toml b/5.Types/5.4 Aliasing/Cargo.toml new file mode 100644 index 0000000..8c0f42c --- /dev/null +++ b/5.Types/5.4 Aliasing/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "aliasing" +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/5.Types/5.4 Aliasing/src/main.rs b/5.Types/5.4 Aliasing/src/main.rs new file mode 100644 index 0000000..2f4bbf3 --- /dev/null +++ b/5.Types/5.4 Aliasing/src/main.rs @@ -0,0 +1,29 @@ +//! +//! type 语句可以给已经存在的类型一个新的别名, +//! 类型别名必须使用大驼峰命名,否则编译器会给出警告, +//! 有一个例外情况就是语言内置的原始类型值没有这个警告,比如 usize,f32,等等 +//! +//! +//! 类型别名通常是用来简化模版代码的, +//! 比如 std::io::Result 就是 std::io::Result 的类型别名 +//! + +// `NanoSecond`, `Inch`, 和 `U64` 都是 `u64` 的类型别名 +type NanoSecond = u64; +type Inch = u64; +type U64 = u64; + +fn main() { + // `NanoSecond` = `Inch` = `U64` = `u64`. + let nanoseconds: NanoSecond = 5 as U64; + let inches: Inch = 2 as U64; + + // 类型别名没有提供任何的额外安全特性, + // 类型别名也没有声明任何新的类型。 + println!( + "{} nanoseconds + {} inches = {} unit?", + nanoseconds, + inches, + nanoseconds + inches + ); +} diff --git a/6.Conversion/6.1 From and Into/Cargo.lock b/6.Conversion/6.1 From and Into/Cargo.lock new file mode 100644 index 0000000..8867c35 --- /dev/null +++ b/6.Conversion/6.1 From and Into/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "from_and_into" +version = "0.1.0" diff --git a/6.Conversion/6.1 From and Into/Cargo.toml b/6.Conversion/6.1 From and Into/Cargo.toml new file mode 100644 index 0000000..82fde51 --- /dev/null +++ b/6.Conversion/6.1 From and Into/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "from_and_into" +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/6.Conversion/6.1 From and Into/src/main.rs b/6.Conversion/6.1 From and Into/src/main.rs new file mode 100644 index 0000000..d2b875e --- /dev/null +++ b/6.Conversion/6.1 From and Into/src/main.rs @@ -0,0 +1,70 @@ +//! +//! `From` 和 `Into` +//! +//! From 和 Into 本质上是互相转换的关系, +//! 比如 A类型可以通过B类型转换过来的话,那么也应该可以从A类型转换到B类型 +//! + +/// From 特性(trait) 允许我们使用一个明确的其他类型值来实例化一个当前的类型 +/// 标准库提供的很多类型都实现了该特性,用于转换原始类型和公共类型 +/// +/// 比如 我们可以非常简单的使用 str 转换成 String 类型 +/// ``` +/// let my_str = "hello"; +/// let my_string = String::from(my_str); +/// ``` +/// +fn example_from() { + #[derive(Debug)] + struct Number { + value: i32, + } + + // 通过实现指定类型的 From 特性的 from 方法可以很容易的实现通过指定类型构造实例 + impl From for Number { + fn from(value: i32) -> Self { + Number { value } + } + } + + let i = 0; + + // 因为实现了 From 特性,所以可以直接使用 into 方法来进行转换 + let num: Number = i.into(); + let num = Number::from(30); + + println!("My Number is {:?}", num); +} + +/// Into 特性就是 From 的逆变实现, +/// 如果对应的类型已经实现了 From 特性的话, +/// +/// 在使用 Into 的时候需要手动指定一个类型,因为编译器并不清楚你想要转换的目标类型是什么 +/// +fn example_into() { + #[derive(Debug)] + struct Number { + value: i32, + } + + // 如果指定的类型已经实现了 From 特性的话 就不用手动再次实现 Into 特性了 + impl Into for i32 { + fn into(self) -> Number { + Number { value: self } + } + } + + let int = 5; + + // 手动指定类型进行转换 + let num: Number = int.into(); + // 通过 Trait 对象,明确给出目标类型,调用转换方法 + let num = Into::::into(int); + + println!("My number is {:?}", num); +} + +fn main() { + example_from(); + example_into(); +} diff --git a/6.Conversion/6.2 TryFrom and TryInto/Cargo.lock b/6.Conversion/6.2 TryFrom and TryInto/Cargo.lock new file mode 100644 index 0000000..077a7d7 --- /dev/null +++ b/6.Conversion/6.2 TryFrom and TryInto/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "try_from_and_try_into" +version = "0.1.0" diff --git a/6.Conversion/6.2 TryFrom and TryInto/Cargo.toml b/6.Conversion/6.2 TryFrom and TryInto/Cargo.toml new file mode 100644 index 0000000..4594449 --- /dev/null +++ b/6.Conversion/6.2 TryFrom and TryInto/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "try_from_and_try_into" +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/6.Conversion/6.2 TryFrom and TryInto/src/main.rs b/6.Conversion/6.2 TryFrom and TryInto/src/main.rs new file mode 100644 index 0000000..a490f02 --- /dev/null +++ b/6.Conversion/6.2 TryFrom and TryInto/src/main.rs @@ -0,0 +1,38 @@ +//! +//! TryFrom\TryInto 和 From\Into 非常相似, +//! 只不过 TryFrom\TryInto 会使用 Result 来包装可能失败的结果 +//! + +use std::convert::TryFrom; +use std::convert::TryInto; + +#[derive(Debug, PartialEq)] +struct EvenNumber(i32); + +impl TryFrom for EvenNumber { + // 指明错误的具体类型 + type Error = (); + + // 使用 Result 来包装结果成功还是失败 + fn try_from(value: i32) -> Result { + if value % 2 == 0 { + Ok(EvenNumber(value)) + } else { + Err(()) + } + } +} + +fn main() { + // TryFrom + + assert_eq!(EvenNumber::try_from(8), Ok(EvenNumber(8))); + assert_eq!(EvenNumber::try_from(5), Err(())); + + // TryInto + + let result: Result = 8i32.try_into(); + assert_eq!(result, Ok(EvenNumber(8))); + let result: Result = 5i32.try_into(); + assert_eq!(result, Err(())); +} diff --git a/6.Conversion/To and From Strings/Cargo.lock b/6.Conversion/To and From Strings/Cargo.lock new file mode 100644 index 0000000..ee42218 --- /dev/null +++ b/6.Conversion/To and From Strings/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "to_and_from_strings" +version = "0.1.0" diff --git a/6.Conversion/To and From Strings/Cargo.toml b/6.Conversion/To and From Strings/Cargo.toml new file mode 100644 index 0000000..4abd18b --- /dev/null +++ b/6.Conversion/To and From Strings/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "to_and_from_strings" +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/6.Conversion/To and From Strings/src/main.rs b/6.Conversion/To and From Strings/src/main.rs new file mode 100644 index 0000000..9c611c0 --- /dev/null +++ b/6.Conversion/To and From Strings/src/main.rs @@ -0,0 +1,39 @@ +use std::{fmt, str::FromStr}; + +struct Circle { + radius: i32, +} + +/// 很多时候需要把数据结构转化成字符串,Rust提供了 ToString 特性, +/// 只需要实现 ToString 特性就可以把结构转换成 String类型, +/// 但是这个方法只能给结构体提供一个 to_string() 方法, +/// 而且不能自动支持类似 println!() 的使用, +/// 通常我们实现的是 fmt::Display 特性,实现了该特性,会自动实现 ToString 特性 +impl fmt::Display for Circle { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Circle {{ radius: {} }}", self.radius) + } +} + +/// 实现 FromStr 的话,就可以通过 str 来构造对应的类型了 +impl FromStr for Circle { + type Err = (); + fn from_str(s: &str) -> Result { + match s.parse() { + Ok(v) => Ok(Circle { radius: v }), + Err(_) => Err(()), + } + } +} + +fn main() { + let circle = Circle { radius: 6 }; + + // 通过 parse 方法传递泛型来通过字符串构造我们的自定义类型 + let circle2 = "6".parse::().unwrap(); + + // 使用 通过实现 fmt::Dispaly 特性提供的 to_string() 方法来获得字符串 + println!("{}", circle.to_string()); + + assert_eq!(circle.radius, circle2.radius) +} diff --git a/7.Expressions/expressions/Cargo.toml b/7.Expressions/expressions/Cargo.toml new file mode 100644 index 0000000..c395bc9 --- /dev/null +++ b/7.Expressions/expressions/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "expressions" +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/7.Expressions/expressions/src/main.rs b/7.Expressions/expressions/src/main.rs new file mode 100644 index 0000000..01fc96f --- /dev/null +++ b/7.Expressions/expressions/src/main.rs @@ -0,0 +1,42 @@ +//! # 表达式 + +/// 大多数情况下 Rust 程序都是由一系列语句组成的 +fn main() { + // 语句 + // 语句 + // 语句 +} + +/// Rust 中最常用的两种是 变量声明和表达式(表达式使用`;`结尾) +fn main() { + // 声明变量绑定到表达式结果 + let x = 5; + + // 单一的表达式; + x; + x + 1; + 15; +} + +/// 多行表达式也是一样的,多行表达式返回的是最后一行的表达式结果, +/// 如果最后一行表达式使用了`;`号结尾的话,则该表达式块会返回 `()` +fn main() { + let x = 5u32; + + let y = { + let x_squared = x * x; + let x_cube = x_squared * x; + + // This expression will be assigned to `y` + x_cube + x_squared + x + }; + + let z = { + // The semicolon suppresses this expression and `()` is assigned to `z` + 2 * x; + }; + + println!("x is {:?}", x); + println!("y is {:?}", y); + println!("z is {:?}", z); +} diff --git a/8.Flow of Control/flow_of_control/Cargo.lock b/8.Flow of Control/flow_of_control/Cargo.lock new file mode 100644 index 0000000..50e20cc --- /dev/null +++ b/8.Flow of Control/flow_of_control/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "flow_of_control" +version = "0.1.0" diff --git a/8.Flow of Control/flow_of_control/Cargo.toml b/8.Flow of Control/flow_of_control/Cargo.toml new file mode 100644 index 0000000..0939515 --- /dev/null +++ b/8.Flow of Control/flow_of_control/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "flow_of_control" +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/8.Flow of Control/flow_of_control/src/main.rs b/8.Flow of Control/flow_of_control/src/main.rs new file mode 100644 index 0000000..48b1403 --- /dev/null +++ b/8.Flow of Control/flow_of_control/src/main.rs @@ -0,0 +1,717 @@ +//! 控制流 + +/// 分支控制 if-else 和大多数语言一样, +/// 但是 不需要使用 () 来包围逻辑表达式 +/// 每一个分支都使用一个 {} 来包围语句块, +/// 因为 if-else 本身也是一个表达式,所以要求所有分支返回相同类型的值 +fn if_else() { + let n = 5; + + if n < 0 { + print!("{} is negative", n); + } else if n > 0 { + print!("{} is positive", n); + } else { + print!("{} is zero", n); + } + + let big_n = if n < 10 && n > -10 { + println!(", and is a small number, increase ten-fold"); + + // 表达式返回 `i32` 类型的值. + 10 * n + } else { + println!(", and is a big number, halve the number"); + + // 这里也需要返回 `i32` 类型的值. + n / 2 + // TODO ^ 通过添加`;`来让语句强制返回 `()` 看看会发生什么. + }; + // ^ 因为这里使用了 let x = if {} else {} 的语法,所以这里必须使用 `;` 结尾. + + println!("{} -> {}", n, big_n); +} + +/// Rust 提供了一个关键字 `loop` 来实现一个无限循环的语句 +/// 可以通过 `continue` 和 `break` 关键字来控制循环的跳过和终止 +fn loop_() { + let mut count = 0u32; + + println!("Let's count until infinity!"); + + // 无限循环的语句 + loop { + count += 1; + + if count == 3 { + println!("three"); + + // 跳过当前的循环,直接执行下一次循环 + continue; + } + + println!("{}", count); + + if count == 5 { + println!("OK, that's enough"); + + // 使用 `break` 关键字手动退出循环 + break; + } + } +} + +/// 循环语句也可以通过使用 `label` 来标记循环体 +/// 通过 `continue` 和 `break` 传递标记的 `label` 来控制循环的执行 +fn nesting_and_labels() { + #![allow(unreachable_code, unused_labels)] + + 'outer: loop { + println!("Entered the outer loop"); + + 'inner: loop { + println!("Entered the inner loop"); + + // 不给 break 传递参数的话 默认只会退出当前最近一层的循环 + //break; + + // 如果传递了 label 标记 则会直接退出标记的循环,不关心循环嵌套了多少层 + break 'outer; + } + + println!("This point will never be reached"); + } + + println!("Exited the outer loop"); +} + +/// 同样的 `loop` 也可以作为语句使用 +/// 通过 `break` 传递语句的最终返回值 +fn returning_from_loops() { + let mut counter = 0; + + let result = loop { + counter += 1; + + if counter == 10 { + break counter * 2; + } + }; + + assert_eq!(result, 20); + println!("loop result is: {}", result); +} + +/// while 关键字提供了一个表达式为 true 就不断重复执行代码块的能力 +fn while_() { + // 定义计数变量 + let mut n = 1; + + // 当 n < 101 的时候就会不断地执行语句块的代码 + while n < 101 { + if n % 15 == 0 { + println!("fizzbuzz"); + } else if n % 3 == 0 { + println!("fizz"); + } else if n % 5 == 0 { + println!("buzz"); + } else { + println!("{}", n); + } + + // 增加计数器 + n += 1; + } + + println!("n is: {}", n); +} + +/// for in 可以使用 `Iterator(迭代器)` 来不断地从迭代器中每次提取一个数据, +/// 在 Rust 中迭代器非常容易创建,可以通过 `a..b` 来快捷的创建一个从 a 开始(包含a)一直到 b (不包含b) 的迭代器 +fn for_and_range() { + // n 会从 0 开始一直到 100 + for n in 1..101 { + if n % 15 == 0 { + println!("fizzbuzz"); + } else if n % 3 == 0 { + println!("fizz"); + } else if n % 5 == 0 { + println!("buzz"); + } else { + println!("{}", n); + } + } + + // 迭代器还可以使用 `a..=b` 的语法,这个迭代器表示从 a 开始(包含a)一直到 b 结束(包含b) + // `n` 会从 1 开始一直到 100 + for n in 1..=100 { + if n % 15 == 0 { + println!("fizzbuzz"); + } else if n % 3 == 0 { + println!("fizz"); + } else if n % 5 == 0 { + println!("buzz"); + } else { + println!("{}", n); + } + } +} + +/// for in 语法还可以通过很多种方法迭代器(Iterator),迭代器特性还可以通过手动实现 Iterator 特性来实现 +/// for in 语句在默认的情况下会调用 into_iter() 来把连续的集合转换为迭代器 +/// +/// 最常用的三种是,into_iter(), iter(), iter_mut() +fn for_and_iterators() { + // iter() 方法 + // 这个方法是对原始数据创建一个数据借用的迭代器,每一次迭代都能获取到当前元素的借用值 + let names = vec!["Bob", "Frank", "Ferris"]; + for name in names.iter() { + match name { + &"Ferris" => println!("There is a rustacean among us!"), + // TODO ^ Try deleting the & and matching just "Ferris" + _ => println!("Hello {}", name), + } + } + // 因为使用的是借用迭代 所以 names 还能继续使用 + println!("names: {:?}", names); + + // into_iter() 方法 + // 该方法会直接转移元素的所有权到迭代器中, + // 调用完该方法以后,原始的变量就不能再次使用了,因为所有权转移了 + let names = vec!["Bob", "Frank", "Ferris"]; + + for name in names.into_iter() { + match name { + "Ferris" => println!("There is a rustacean among us!"), + _ => println!("Hello {}", name), + } + } + + // println!("names: {:?}", names); + // FIXME ^ 注释这行代码 因为 into_iter() 方法转移所有权到迭代器内部了 所以 names 不能使用了 + + // iter_mut() 方法 + // 该方法会创建一个可变借用的迭代器,也就是在迭代过程中可以对原始数据进行修改, + // 因为是借用关系所以迭代以后,原始变量依旧有效 + let mut names = vec!["Bob", "Frank", "Ferris"]; + + for name in names.iter_mut() { + *name = match name { + &mut "Ferris" => "There is a rustacean among us!", + _ => "Hello", + } + } + + println!("names: {:?}", names); +} + +/// Rust 通过 `match` 关键字提供了模式匹配的能力 +/// 该模式类似于 C 的 `switch` 语句,第一个匹配的模式代码会被执行, +/// 并且必须所有可能的情况都要覆盖到。 +fn match_() { + let number = 13; + // TODO ^ 尝试其他的整数值 + + println!("Tell me about {}", number); + match number { + // 匹配固定的一个数字 1 + 1 => println!("One!"), + // 匹配多种可能得数字 + 2 | 3 | 5 | 7 | 11 => println!("This is a prime"), + // TODO ^ 尝试把数字 13 添加到质数列表中 + // 匹配一个固定的区间范围 + 13..=19 => println!("A teen"), + // 处理剩余其他可能的值 + _ => println!("Ain't special"), + // TODO ^ 尝试注释掉最后的这个分支 + } + + let boolean = true; + // 还可以匹配 bool 表达式 + let binary = match boolean { + // 匹配的模式必须覆盖到所有可能的结果 + false => 0, + true => 1, + // TODO ^ 尝试注释掉一个分支 + }; + + println!("{} -> {}", boolean, binary); +} + +// match 关键字还可以匹配元组 +fn match_tuples() { + let triple = (0, -2, 3); + // TODO ^ 尝试不同的元组值 + + println!("Tell me about {:?}", triple); + // match 表达式中可以对元组进行解构 + match triple { + // 第一个元素是 0 的情况下,把第二个值命名为 y, 第三个值命名为 z + (0, y, z) => println!("First is `0`, `y` is {:?}, and `z` is {:?}", y, z), + (1, ..) => println!("First is `1` and the rest doesn't matter"), + (.., 2) => println!("last is `2` and the rest doesn't matter"), + (3, .., 4) => println!("First is `3`, last is `4`, and the rest doesn't matter"), + // `..` 关键字可以忽略任意数量的元组值 + _ => println!("It doesn't matter what they are"), + // `_` 关键字表示匹配任意的值 + } +} + +// match关键字还可以匹配数组和切片 +fn match_array_slice() { + // 尝试修改数组,或者把数组换成切片 + let array = [1, -2, 6]; + + match array { + // 当第一个元素是0,把第二个元素命名为 second, 第三个元素命名为 third + [0, second, third] => println!("array[0] = 0, array[1] = {}, array[2] = {}", second, third), + + // 当第一个元素是0,忽略第二个元素,把第三个元素命名为 third + [1, _, third] => println!( + "array[0] = 1, array[2] = {} and array[1] was ignored", + third + ), + + // 当第一个元素是-1,把第二个元素命名为 second,忽略后面的所有值 + [-1, second, ..] => println!( + "array[0] = -1, array[1] = {} and all the other ones were ignored", + second + ), + // 这个分支不能被编译,因为没有正确的匹配上数组的数量 + // [-1, second] => println!("The code below would not compile"), + + // 当第一个元素是3,把第二个元素命名为 second,把剩余的所有数据收集到一个数组或切片中(类型取决于传递进来的数据类型) + [3, second, tail @ ..] => println!( + "array[0] = 3, array[1] = {} and the other elements were {:?}", + second, tail + ), + + // 合并上面的用法到一起 + // 绑定 第一个元素到 first,最后一个元素到 last,中间剩余的所有的放到 middle 中 + [first, middle @ .., last] => println!( + "array[0] = {}, middle = {:?}, array[2] = {}", + first, middle, last + ), + } +} + +// 匹配枚举值 +fn match_enum() { + // 使用 `allow` 宏禁用未使用的代码警告 + #[allow(dead_code)] + enum Color { + // 这三个值只定义名字 + Red, + Blue, + Green, + // 用来表示颜色不同数据结构,都使用 u32 类型来保存颜色信息 + RGB(u32, u32, u32), + HSV(u32, u32, u32), + HSL(u32, u32, u32), + CMY(u32, u32, u32), + CMYK(u32, u32, u32, u32), + } + + let color = Color::RGB(122, 17, 40); + // TODO ^ 尝试不同类型的枚举值 + + println!("What color is it?"); + // 枚举可以通过 `match` 关键字来解构 + match color { + Color::Red => println!("The color is Red!"), + Color::Blue => println!("The color is Blue!"), + Color::Green => println!("The color is Green!"), + Color::RGB(r, g, b) => println!("Red: {}, green: {}, and blue: {}!", r, g, b), + Color::HSV(h, s, v) => println!("Hue: {}, saturation: {}, value: {}!", h, s, v), + Color::HSL(h, s, l) => println!("Hue: {}, saturation: {}, lightness: {}!", h, s, l), + Color::CMY(c, m, y) => println!("Cyan: {}, magenta: {}, yellow: {}!", c, m, y), + Color::CMYK(c, m, y, k) => println!( + "Cyan: {}, magenta: {}, yellow: {}, key (black): {}!", + c, m, y, k + ), + // 因为枚举中的所有项都已经匹配完了,不需要再写其他的匹配项了 + } +} + +/// 指针和引用 +/// 对于指针来说,不像是在C/C++中使用方式,`解构`和`解引用`是两个不同的概念 +/// +/// 解引用使用的是 `*` +/// 解构使用的是 `&`,`ref` 和 `ref mut` +/// +fn match_points_or_ref() { + // 这里声明的是一个 i32 类型的引用值,也就是说 reference是 `&i32` 类型 + let reference = &4; + + match reference { + // 如果 `reference` 和 `&val` 匹配的话,匹配的过程应该是这样 + // &val = &i32 因为都有相同的 `&` 符号,所以相同的符号被移除掉,带入数值的计算过程如下 + // &val = &i32 => &val = &4 => val = 4 + // 所以最终val的值就是 4 + &val => println!("Got a value via destructuring: {:?}", val), + } + + // 如果想避免在 match 的匹配模式中使用 `&` 的话,可以提前进行解引用 + match *reference { + val => println!("Got a value via dereferencing: {:?}", val), + } + + // 如果不想声明引用则声明的时候可以不写引用`&`符号 + let _not_a_reference = 3; + + // Rust 提供了另外一个操作符来让一个非引用的值强制转换成引用值 + // 通过在声明变量前面添加 `ref` 关键字,强制让变量是一个引用 + // 该方法在 match 语句中经常使用 + let ref _is_a_reference = 3; + + // 如果一个变量不是引用,但是可以通过添加 `ref` `ref mut` 强制让他转换成一个引用 + let value = 5; + let mut mut_value = 6; + + // 使用 `ref` 关键字创建一个不可变引用 + match value { + ref r => println!("Got a reference to a value: {:?}", r), + } + + // 使用 `ref mut` 关键字创建一个可变引用. + match mut_value { + ref mut m => { + // 得到一个可变的引用类型,通过解引用来修改原始值 + *m += 10; + println!("We added 10. `mut_value`: {:?}", m); + } + } +} + +// match 还可以匹配结构体 +fn match_struct() { + struct Foo { + x: (u32, u32), + y: u32, + } + + // 修改结构体中的数据,看看会发生什么 + let foo = Foo { x: (1, 2), y: 3 }; + + match foo { + Foo { x: (1, b), y } => println!("First of x is 1, b = {}, y = {} ", b, y), + + // 结构的过程可以对字段重命名,解构中字段的书写顺序不会影响结果 + Foo { y: 2, x: i } => println!("y is 2, i = {:?}", i), + + // 也可以使用 `..` 关键字忽略一些不需要的字段 + Foo { y, .. } => println!("y = {}, we don't care about x", y), + // 下面这个分支会报错,因为结构的时候并没有覆盖所有的字段 + // Foo { y } => println!("y = {}", y), + } + + let faa = Foo { x: (1, 2), y: 3 }; + + // 还可以使用 let 语句直接对进行解构 + let Foo { x: x0, y: y0 } = faa; + println!("Outside: x0 = {x0:?}, y0 = {y0}"); +} + +// 增加 match 分支的匹配条件 +fn match_guards() { + { + #[allow(dead_code)] + enum Temperature { + Celsius(i32), + Fahrenheit(i32), + } + + let temperature = Temperature::Celsius(35); + // ^ TODO 尝试 Temperature 其他的枚举值 + + match temperature { + Temperature::Celsius(t) if t > 30 => println!("{}C is above 30 Celsius", t), + // 当上面的条件不命中的时候就会走到下面这个分支中 + Temperature::Celsius(t) => println!("{}C is below 30 Celsius", t), + + Temperature::Fahrenheit(t) if t > 86 => println!("{}F is above 86 Fahrenheit", t), + Temperature::Fahrenheit(t) => println!("{}F is below 86 Fahrenheit", t), + } + } + + { + let number: u8 = 4; + + // 编译器检查match的条件是否覆盖了所有可能的时候会忽略掉额外的条件判断 + match number { + i if i == 0 => println!("Zero"), + i if i > 0 => println!("Greater than zero"), + _ => unreachable!("Should never happen."), + // TODO ^ 取消上面这行注释 + } + } +} + +// 动态值绑定 +fn match_binding() { + { + // `age`函数返回一个u32类型的值 + fn age() -> u32 { + 15 + } + println!("Tell me what type of person you are"); + + match age() { + 0 => println!("I haven't celebrated my first birthday yet"), + // 绑定区间的时候最大的问题就是,命中了区间确无法知道具体命中的值是多少 + // @ 操作符可以让你把命中区间的值绑定到一个变量上 + n @ 1..=12 => println!("I'm a child of age {:?}", n), + n @ 13..=19 => println!("I'm a teen of age {:?}", n), + // 这里不是区间值所以不需要重新绑定. Return the result. + n => println!("I'm an old person of age {:?}", n), + } + } + + { + fn some_number() -> Option { + Some(42) + } + match some_number() { + // 同样的也可以使用在任意的枚举值中 + // 当值等于 42 的时候,n 等于 42 有点脱裤子放屁了 + Some(n @ 42) => println!("The Answer: {}!", n), + // 任意的其他值绑定到 n 上 + Some(n) => println!("Not interesting... {}", n), + // None 走到兜底匹配 + _ => (), + } + } +} + +fn if_let() { + // 基础的 match 表达式 + { + // 创建一个 Option 类型 + let optional = Some(7); + + match optional { + Some(i) => { + println!("This is a really long string and `{:?}`", i); + // 使用 match 关键字 解构 Option 中的数据 + } + _ => {} // 这个统配的分支存在是因为 match 操作符必须要处理所有的可能结果 + }; + } + + // 使用 `if let` 表达式尝试解构并执行一些语句 + // 一旦使用了 `if let` 表达式,编译器就不会像 `match` 操作符一样做所有情况的检查,也就可能导致某些情况没有覆盖到 + { + // 全都定义为 `Option` + let number = Some(7); + let letter: Option = None; + let emoticon: Option = None; + + // `if let` 解构的语义化读法是,如果 `number` 可以被 `Some(i)` 解构的话,则执行对应的 {} 代码块 + if let Some(i) = number { + println!("Matched {:?}!", i); + } + + // 如果需要知道结构失败的情况的话直接添加 `else {}` 分支即可 + if let Some(i) = letter { + println!("Matched {:?}!", i); + } else { + // 如果解构失败的话执行这些代码 + println!("Didn't match a number. Let's go with a letter!"); + } + + // 解构失败以后的额外判断条件 + let i_like_letters = false; + + if let Some(i) = emoticon { + println!("Matched {:?}!", i); + // 如果解构失败的话,会继续尝试下一个 `else if` 的逻辑判断 + // 在这里依旧会失败因为 `i_like_letters` 的值是false + } else if i_like_letters { + println!("Didn't match a number. Let's go with a letter!"); + } else { + // 最后会走到 `else {}` 分支中 + println!("I don't like letters. Let's go with an emoticon :)!"); + } + } + + // 同样的 `if let` 表达式还可以尝试解构 `枚举(enum)` + { + // 用于 + enum Foo { + Bar, + Baz, + Qux(u32), + } + + // 创建几个枚举变量 + let a = Foo::Bar; + let b = Foo::Baz; + let c = Foo::Qux(100); + + // `使用 if let` 解构变量,因为 `a` 就是 `Foo:Bar` 类型,所以会执行 {} 中的语句 + if let Foo::Bar = a { + println!("a is foobar"); + } + + // 变量 `b` 不能解构为 `Foo::Bar` + // 所以 {} 中的语句不会被执行 + if let Foo::Bar = b { + println!("b is foobar"); + } + + // 变量 `c` 可以解构为 `Foo::Qux(value)` + // 类似于 `if let Some(value) = x {}` 一样 + if let Foo::Qux(value) = c { + println!("c is {}", value); + } + + // 特定的 value 值限定也是支持的 + // 下面是支持的三种语法特定值的限定语法 + if let Foo::Qux(value @ 100) = c { + println!("c is one hundred"); + } + if let Foo::Qux(value @ (0..=100)) = c { + println!("c is one hundred"); + } + if let Foo::Qux(value @ (0 | 100)) = c { + println!("c is one hundred"); + } + } + + // `if let` 特性还有一个好处就是可以不用实现 `PartialEq` 特性就能判断类型是否一致 + { + // 这个枚举没有实现 `PartialEq` 特性,所以下面的 if 语句会报错 + enum Foo { + Bar, + } + + let a = Foo::Bar; + + // 因为 Foo 类型没有实现 PartialEq 所以会报错 + // if Foo::Bar == a { + // // ^-- 编译报错,可以尝试使用 `if let` 替换 + // println!("a is foobar"); + // } + } +} + +/// Rust 1.65 以后这个特性才规范为正式语法 +/// 这个特性实际上就是 if let Some(value) = x { value } else { ! } 的语法糖 +/// 这个特性的常用情况是,解构指定的类型出错的情况下需要执行的额外代码, +/// 比如 `break`,`contiune`,`panic!()` 等等情况 +fn let_else() { + { + use std::str::FromStr; + fn get_count_item(s: &str) -> (u64, &str) { + let mut it = s.split(' '); + // 这里当字符串读取失败了,那就直接 `panic!` 不需要写额外的逻辑了 + let (Some(count_str), Some(item)) = (it.next(), it.next()) else { + panic!("Can't segment count item pair: '{s}'"); + }; + // 这里同理 + let Ok(count) = u64::from_str(count_str) else { + panic!("Can't parse integer: '{count_str}'"); + }; + (count, item) + } + + assert_eq!(get_count_item("3 chairs"), (3, "chairs")); + } + + // 上面的代码使用 `if let` 来写的话是下面这样的,看起来没有上面的更清晰 + { + use std::str::FromStr; + fn get_count_item(s: &str) -> (u64, &str) { + let mut it = s.split(" "); + let (count_str, item) = match (it.next(), it.next()) { + (Some(count_str), Some(item)) => (count_str, item), + _ => panic!("Can't segment count item pair: '{s}'"), + }; + let count = if let Ok(count) = u64::from_str(count_str) { + count + } else { + panic!("Can't parse integer: '{count_str}'"); + }; + (count, item) + } + + assert_eq!(get_count_item("3 chairs"), (3, "chairs")); + } +} + +fn while_let() { + { + // 创建 `Option` 类型变量 + let mut optional = Some(0); + + // 重复执行 `match` + loop { + match optional { + // 如果 `optional` 可以被 Some 解构则执行代码 + Some(i) => { + if i > 9 { + println!("Greater than 9, quit!"); + optional = None; + } else { + println!("`i` is `{:?}`. Try again.", i); + optional = Some(i + 1); + } + // ^ 这里需要三级缩进了 + } + // 如果解构失败则退出循环 + _ => { + break; + } // ^ 这个写法太啰嗦了,应该有更好的写法 + } + } + } + + // 为了解决上面的语法啰嗦问题,这里有个简化版的语法糖 + { + // 创建 `Option` 类型变量 + let mut optional = Some(0); + + // 重复使用 `Some` 尝试解构 `optional`, + // 如果解构成功的话就一直执行代码块,否则退出循环 + while let Some(i) = optional { + if i > 9 { + println!("Greater than 9, quit!"); + optional = None; + } else { + println!("`i` is `{:?}`. Try again.", i); + optional = Some(i + 1); + } + // 代码清晰了很多,也少了缩进 + } + // `if let` 语句允许我们使用 `else if`, `else` 来处理更多额外的逻辑 + // `while let` 不支持 `else if`, `else` + } +} + +fn main() { + // 分支语句 + if_else(); + + // 循环语句 + loop_(); + nesting_and_labels(); + returning_from_loops(); + while_(); + for_and_range(); + for_and_iterators(); + + // 匹配语句 + match_(); + match_tuples(); + match_array_slice(); + match_enum(); + match_points_or_ref(); + match_struct(); + match_guards(); + match_binding(); + + // 判断解构 + if_let(); + let_else(); + while_let(); +} diff --git a/9.Functions/Cargo.lock b/9.Functions/Cargo.lock new file mode 100644 index 0000000..82f83a1 --- /dev/null +++ b/9.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/9.Functions/Cargo.toml b/9.Functions/Cargo.toml new file mode 100644 index 0000000..a9b5578 --- /dev/null +++ b/9.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/9.Functions/src/main.rs b/9.Functions/src/main.rs new file mode 100644 index 0000000..776de8f --- /dev/null +++ b/9.Functions/src/main.rs @@ -0,0 +1,321 @@ +//! +//! # Functions +//! 函数使用 `fn` 关键字定义。必须指定参数类型和数量, +//! 如果函数有返回值的话,必须使用 `->` 把返回值定义在后面 +//! +//! 函数的最后一个表达式的返回值会自动当做函数的返回值, +//! 也可以使用 `return` 关键字在代码的任意位置(if\match\loop\等等)让函数提前退出并返回值 +//! + +fn functions() { + // 返回 bool 值的函数 + fn is_divisible_by(lhs: u32, rhs: u32) -> bool { + // 边界值直接返回 false + if rhs == 0 { + return false; + } + + // 这个是最后的语句 可以不使用 `return` 关键字,因为语句没有使用 `;` 结尾, + // 所以函数会使用最后的语句返回值当做整个函数的返回值 + lhs % rhs == 0 + } + + // 这个函数没有返回值,等于返回一个 `()` 空元组(特殊的元组又叫 Unit Value) + fn fizzbuzz(n: u32) -> () { + if is_divisible_by(n, 15) { + println!("fizzbuzz"); + } else if is_divisible_by(n, 3) { + println!("fizz"); + } else if is_divisible_by(n, 5) { + println!("buzz"); + } else { + println!("{}", n); + } + } + + // 如果函数的返回值是 `()` 则可以省略返回值的定义 + fn fizzbuzz_to(n: u32) { + for n in 1..=n { + fizzbuzz(n); + } + } + + fizzbuzz_to(100); +} + +/// `关联函数` 和 `关联方法` 这两个概念非常相似 +/// `关联函数` 是自定义类型上面和 `实例无关` 的函数定义 +/// `关联方法` 是自定义类型上面给 `实例定义` 的操作实例的方法 +fn associated_functions_and_methods() { + struct Point { + x: f64, + y: f64, + } + + // Implementation block, all `Point` associated functions & methods go in here + // 为 `Point` 类型实现 `关联方法` 和 `关联函数` 的代码块定义 + impl Point { + // 这个是关联的函数,因为这个函数不需要 `Point` 实例 + // 这个函数只和 `Point` 这个类型有关 + fn origin() -> Point { + Point { x: 0.0, y: 0.0 } + } + + // 另外一个 `关联函数`,这个函数需要两个参数 + fn new(x: f64, y: f64) -> Point { + Point { x: x, y: y } + } + } + + struct Rectangle { + p1: Point, + p2: Point, + } + + impl Rectangle { + // 这个是 `关联方法`,因为函数使用了 `&self` 引用,也就是当前调用函数的实例, + // 这个是一个语法糖,等价于 `self: &Self` + // `Self` 是当前的类型的别名也就是 `Rectangle` + fn area(&self) -> f64 { + // `self` gives access to the struct fields via the dot operator + // 使用 `self` 参数通过 `.` 操作符来引用实例上的属性值 + let Point { x: x1, y: y1 } = self.p1; + let Point { x: x2, y: y2 } = self.p2; + + // `abs` 是 `f64` 类型上的 `关联方法`,所以可以直接通过 `.` 操作符调用 + ((x1 - x2) * (y1 - y2)).abs() + } + + // 这个也是一个 `关联方法` + fn perimeter(&self) -> f64 { + let Point { x: x1, y: y1 } = self.p1; + let Point { x: x2, y: y2 } = self.p2; + + 2.0 * ((x1 - x2).abs() + (y1 - y2).abs()) + } + + // 这个关联方法,使用了可变引用,`&mut self` 就是 `self: &mut Self` 的语法糖 + fn translate(&mut self, x: f64, y: f64) { + self.p1.x += x; + self.p2.x += x; + + self.p1.y += y; + self.p2.y += y; + } + } + + // `Pair` 类型拥有两个堆内存上的i32的整数 + struct Pair(Box, Box); + + impl Pair { + // 这个关联方法转移了 `self` 实例的所有权,`self` 就是 `self: Self` 的语法糖 + fn destroy(self) { + // 解构 `self` 拿到内部属性值 + let Pair(first, second) = self; + + println!("Destroying Pair({}, {})", first, second); + + // 因为 `self` 的所有权转移到了当前作用域, + // `first` 和 `second` 又把 `self` 上面保存的值转移了出来 + // 当这两个变量走出作用域,那这两个变量的内存就会被回收,同时 `self` 也被回收了 + } + } + + let rectangle = Rectangle { + // 关联函数可以使用 `::` 作用域访问操作符来使用 + p1: Point::origin(), + p2: Point::new(3.0, 4.0), + }; + + // 关联方法可以使用类型实例通过 `.` 操作符来调用, + // 关联方法的第一个参数会自动使用当前的实例来填充上去 + // `rectangle.perimeter()` 等价于 `Rectangle::perimeter(&rectangle)` + println!("Rectangle perimeter: {}", rectangle.perimeter()); + println!("Rectangle area: {}", rectangle.area()); + + let mut square = Rectangle { + p1: Point::origin(), + p2: Point::new(1.0, 1.0), + }; + + // 报错!因为 `rectangle` 是不可变对象,但是 `translate` 方法需要一个可变的对象,所以报错了 + // rectangle.translate(1.0, 0.0); + // TODO ^ 解除上面这行注释查看错误 + + // 这里可以,因为 `square` 是一个可变对象 + square.translate(1.0, 1.0); + + let pair = Pair(Box::new(1), Box::new(2)); + + pair.destroy(); + + // Error! Previous `destroy` call "consumed" `pair` + // 报错!因为已经调用了 `destroy` 方法,这个方法会转移 `pair` 对象的所有权, + // 当 `destroy` 方法执行,当前作用域的 `pair` 就已经被转移到了 `destroy` 函数中 + // 当前作用域已经没有 `pair` 对象了,所以会报错 + // pair.destroy(); + // TODO ^ 解除上面这行注释查看错误 +} + +/// 闭包是一个很重要的概念 +/// 闭包和函数非常相似,但是闭包有一个能力就是可以捕获当前上下文的变量,然后延迟执行 +/// 这个特性可以让闭包可以动态使用,比常规函数更加灵活 +/// +/// 闭包的特性和组成部分 +/// - 使用 `||` 替换了 `()` 来代替参数的声明 +/// - 如果只有一行表达式的话可以省略掉 `{}` +/// - 可以捕获声明的作用域中的变量 +/// +fn closures() { + { + let x = 10; + + let closures = |val| { + println!("closure capture variable x is: {}", x); + val + x + }; + + let result = closures(100); + + println!("closures: {}", result); + } + + { + let outer_var = 42; + + // 常规的函数不允许引用外部作用域中的变量 + // fn function(i: i32) -> i32 { + // i + outer_var + // } + // TODO: 移除注释查看错误,编译器会推荐使用闭包 + + // 闭包都是匿名的,我们可以把闭包绑定到一个变量上 + // 闭包的变量和返回值声明和函数的规则相同 + // 如果闭包只有一行表达式,那么闭包的 `{}` 是可以省略的 + let closure_annotated = |i: i32| -> i32 { i + outer_var }; + let closure_inferred = |i| i + outer_var; + + // 调用闭包和调用常规函数是一样的 + println!("closure_annotated: {}", closure_annotated(1)); + println!("closure_inferred: {}", closure_inferred(1)); + + // 如果闭包的类型已经被推断出具体类型了,那么就不允许再次使用其他类型了 + // println!( + // "cannot reuse closure_inferred with another type: {}", + // closure_inferred(42i64) + // ); + // TODO: 移除注释查看编译错误 + + // 这个闭包没有参数,但是有一个返回值,根据整数推断规则,返回值被推断成 `i32` + let one = || 1; + println!("closure returning one: {}", one()); + } +} + +/// 闭包的变量捕获 +/// 闭包本身是非常灵活的,即便没有类型注释, +/// 也可以根据上下文自动推断出类型,并正常运行 +/// +/// 闭包可以自动捕获如下类型 +/// - &T 变量借用 +/// - &mut T 可变变量借用 +/// - T 变量移动 +fn closures_capturing() { + { + use std::mem; + let 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. + let print = || println!("`color`: {}", color); + + // Call the closure using the borrow. + print(); + + // `color` can be borrowed immutably again, because the closure only holds + // an immutable reference to `color`. + let _reborrow = &color; + print(); + + // A move or reborrow is allowed after the final use of `print` + 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. + // let _reborrow = &count; + // ^ TODO: try uncommenting this line. + 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. + 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. + let consume = || { + println!("`movable`: {:?}", movable); + mem::drop(movable); + }; + + // `consume` consumes the variable so this can only be called once. + consume(); + // consume(); + // ^ TODO: Try uncommenting this line. + } + + { + // `Vec` has non-copy semantics. + let haystack = vec![1, 2, 3]; + + 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. + + // 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. + } +} + +fn main() { + // 常规函数 + functions(); + + // 关联函数、关联方法 + associated_functions_and_methods(); + + // 基础闭包 + closures(); + // 闭包捕获 + closures_capturing(); +} diff --git a/update-projects.sh b/update-projects.sh new file mode 100755 index 0000000..b5f8dd3 --- /dev/null +++ b/update-projects.sh @@ -0,0 +1,5 @@ +#!bash + +configFile=".vscode/settings.json" + +find . -name "Cargo.toml" | grep "$1" | jq -Rs --slurpfile c $configFile 'split("\n")|[.[]|select(.!="")]|$c[0]+{"rust-analyzer.linkedProjects": .}' >$configFile