面向 GPT4 学习 Rust

pub(crate) 和 pub 区别

在 Rust 中,pub(crate)pub 关键字用于控制项目中的可见性(或称作访问控制)。它们之间的区别在于它们允许其他模块访问的范围不同。

  1. pub:当你使用 pub 关键字修饰一个项(如函数、结构体、枚举等)时,这意味着该项对整个项目及其依赖该项目的其他项目都是可见的。换句话说,它具有公共可见性,任何外部模块或库都可以访问它。
  2. pub(crate):当你使用 pub(crate) 关键字修饰一个项时,它表示该项仅在当前 crate(包)中可见。这意味着该项对 crate 内的所有模块可见,但不能被其他依赖于此 crate 的项目访问。它具有 crate 级别的可见性,是一个更严格的访问控制。

使用 pub(crate) 而不是 pub 的一个主要原因是,当你想在当前 crate 的多个模块之间共享某个项,但不希望它被外部的 crate 使用时,可以使用 pub(crate)。这有助于维护封装性和避免不必要的公共 API 暴露。

for i in (0..=31).rev() 和 for i in (0..31).rev() 的区别

  • (0..=31).rev() 创建了一个从 0 到 31(包括 31)的逆向范围。这意味着迭代将从 31 开始,然后递减到 0。这是一个闭区间,因为它包括了边界值 31。

  • (0..32).rev() 创建了一个从 0 到 32(不包括 32)的逆向范围。这意味着迭代将从 31 开始,然后递减到 0。这是一个左闭右开区间,因为它不包括边界值 32。

在第一个代码片段中,使用 ..= 语法表示闭区间,而在第二个代码片段中,使用 .. 语法表示左闭右开区间。

Rust 如何 进行单元测试

在 Rust 中,进行单元测试相对简单。通常,您会在源代码文件(例如 lib.rsmain.rs)中编写测试函数。以下是进行单元测试的基本步骤:

  1. 在您要编写测试的源代码文件中,添加一个名为 tests 的模块,并使用 #[cfg(test)] 属性配置它仅在测试配置中编译。这样可以确保在正常构建时不会编译和运行测试代码。
rust
1#[cfg(test)]
2mod tests {
3    // 测试代码将在这里
4}
  1. tests 模块中,为要测试的函数编写测试函数。测试函数应该以 test_ 为前缀,并使用 #[test] 属性标记。在测试函数内部,您可以使用 assert!assert_eq!assert_ne! 等宏来验证结果是否符合预期。

例如,假设您有一个名为 add 的函数:

rust
1fn add(a: i32, b: i32) -> i32 {
2    a + b
3}

您可以编写如下单元测试:

rust
1#[cfg(test)]
2mod tests {
3    use super::*; // 引入当前作用域的所有内容,包括 `add` 函数
4
5    #[test]
6    fn test_add() {
7        assert_eq!(add(1, 2), 3);
8        assert_eq!(add(0, 0), 0);
9        assert_eq!(add(-1, 1), 0);
10    }
11}
  1. 使用 cargo test 命令运行测试。Cargo 将自动找到并运行所有使用 #[test] 属性标记的测试函数。

在终端中键入以下命令:

sh
1cargo test

如果测试成功,您将看到类似以下输出:

bash
1running 1 test
2test tests::test_add ... ok
3
4test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s

这意味着所有测试均已通过。

单元测试是 Rust 中编写可靠代码的关键组成部分。通过编写测试来验证您的函数和模块,您可以确保代码的正确性和稳定性。

在 CLion 中使用 Live Templates 加速开发

  • tmod
rust
1#[cfg(test)]
2mod tests {
3    use super::*;
4    
5    
6}
  • tfn
rust
1#[cfg(test)]
2mod tests {
3    use super::*;
4    
5    #[test]
6    fn test() {
7        
8    }
9}

#[derive(Default)]

在 Rust 编程语言中,#[derive(Default)] 是一个派生宏(derive macro),用于自动实现结构体或枚举的默认值。

通过在结构体或枚举上使用 #[derive(Default)],可以为其自动生成一个默认值。默认值是该类型的每个字段或成员的默认值的组合。

例如,考虑以下结构体的示例:

rust
1#[derive(Default)]
2struct Person {
3    name: String,
4    age: u32,
5    is_student: bool,
6}

通过在结构体上使用 #[derive(Default)],可以为 Person 结构体自动生成一个默认值。默认值将使用每个字段的默认值:

rust
1fn main() {
2    let person: Person = Default::default();
3    println!("Name: {}", person.name);             // 输出: Name:
4    println!("Age: {}", person.age);               // 输出: Age: 0
5    println!("Is Student: {}", person.is_student); // 输出: Is Student: false
6}

在上述示例中,我们创建了一个 Person 结构体的实例 person,并访问其字段。由于我们使用了 #[derive(Default)],因此 person 的字段被初始化为默认值。对于 String 类型的字段 name,默认值是一个空字符串 "";对于 u32 类型的字段 age,默认值是 0;对于 bool 类型的字段 is_student,默认值是 false

#[derive(Default)] 简化了结构体或枚举的默认值的实现,使得代码更简洁,并避免了手动编写默认值的重复工作。

usize

usizei32 是 Rust 编程语言中的两种整数类型,它们在表示方式和用途上有一些差别。

  1. 符号性(Signedness):i32 是有符号整数类型,可以表示正数、负数和零。而 usize 是无符号整数类型,只能表示非负数和零。因此,usize 可用于表示数组索引、集合大小和内存地址等只能是非负数的情况。
  2. 大小:i32 的大小是固定的,它占用 32 位(4 字节)内存空间,无论在何种计算机架构上。而 usize 的大小取决于当前的计算机架构,通常为 32 位或 64 位。在 32 位架构上,usize 大小为 32 位(4 字节),在 64 位架构上,usize 大小为 64 位(8 字节)。
  3. 取值范围:由于 usize 是无符号整数类型,它可以表示的最大值是比 i32 更大的正整数。在 32 位架构上,usize 的最大值是 4294967295,而 i32 的最大值是 2147483647

由于 usizei32 有不同的符号性和大小,因此在使用它们时需要根据具体的需求和上下文来选择合适的类型。通常情况下,usize 适用于索引、集合大小和内存相关的操作,而 i32 适用于一般的整数运算和表示。

#[derive(Debug)]

#[derive(Debug)] 是 Rust 编程语言中的一个派生宏(derive macro),用于为自定义的结构体或枚举类型自动生成实现 Debug trait 的代码。

Debug trait 是 Rust 标准库提供的一个 trait,用于格式化输出调试信息。通过实现 Debug trait,我们可以使用调试格式化字符串打印自定义类型的值。

使用 #[derive(Debug)] 宏,我们可以简单地为结构体或枚举类型自动生成 Debug trait 的实现,而无需手动编写代码。

例如,考虑以下结构体的示例:

rust
1#[derive(Debug)]
2struct Person {
3    name: String,
4    age: u32,
5    is_student: bool,
6}

通过在结构体上使用 #[derive(Debug)],我们为 Person 结构体自动生成了 Debug trait 的实现。这使得我们可以直接使用调试格式化字符串打印 Person 类型的值:

rust
1fn main() {
2    let person = Person {
3        name: "Alice".to_string(),
4        age: 25,
5        is_student: true,
6    };
7
8    println!("{:?}", person);
9}

在上述示例中,我们创建了一个 Person 结构体的实例 person,并通过使用 println!("{:?}", person) 来打印它的调试信息。由于我们使用了 #[derive(Debug)],因此编译器会自动生成打印 person 的调试格式化字符串。输出结果将类似于以下内容:

yaml
1Person { name: "Alice", age: 25, is_student: true }

通过自动实现 Debug trait,我们可以方便地打印自定义类型的调试信息,用于调试和排查问题。