15天100题入门Rust——Day1

在各种编程语言的学习中,”反复入门”是许多学习者的共同难题,尤其是对于Rust这样的系统编程语言。Rust以其独特的所有权模型、零成本抽象和内存安全特性著称,但这些也使得它的学习曲线相对陡峭。因此,很多开发者在初次尝试后,可能会发现自己不得不重新开始学习Rust。

为了解决这个问题,我设计了一个15天的Rust学习计划,旨在通过15个精心挑选的主题,带领大家逐步深入Rust的世界。每天,我们将专注于一个核心主题,并通过一系列实践任务来巩固学习成果。每个任务都会有相应的答案和详细解释,即使在遇到挑战时,你也能够找到方向和答案。

今天,我们将从Rust的基础语法开始,这是任何Rust项目的基石。我们会一起探索变量绑定、函数、基本类型、注释等概念,并通过实际的编码练习来加深理解。这些基础知识将为接下来的学习打下坚实的基础,让你在接下来的14天里,能够更加自信地探索Rust的高级特性。

下面是这15天的内容大纲:

  1. Rust的基本语法:了解变量绑定、函数、基本类型、注释等。

  2. 所有权(Ownership):这是Rust最核心的概念之一,包括所有权规则、借用(borrowing)、生命周期(lifetimes)。

  3. 类型系统:理解不可变性(immutability)、结构体(structs)、枚举(enums)、泛型(generics)和特征(traits)。

  4. 错误处理:学习使用ResultOption枚举来处理可能的错误。

  5. 模式匹配:掌握如何使用matchif let来进行模式匹配。

  6. 集合类型:熟悉Rust的标准集合类型,如向量(Vec)、字符串(String)、哈希映射(HashMap)等。

  7. 迭代器:理解迭代器(iterators)的使用和创建,以及它们如何与闭包(closures)配合使用。

  8. 并发编程:学习Rust的线程(threads)、消息传递(message passing)、共享状态(shared state)、以及async/.await

  9. 模块系统:了解如何组织代码和模块,使用moduse声明。

  10. 包管理和Crates.io:学习如何使用Cargo进行包管理,以及如何在Crates.io上发布库。

  11. :了解宏(macros)的基础知识,它们如何用于代码生成。

  12. FFI(Foreign Function Interface):学习如何使用Rust与其他语言编写的代码交互。

  13. 测试:掌握如何编写测试来验证你的代码。

  14. 文档:学习如何为你的代码编写文档注释。

  15. Rust的生态系统:熟悉一些流行的Rust库和框架,以及如何在项目中使用它们。


Day1 问题

Rust的基本语法:了解变量绑定、函数、基本类型、注释等。

  1. 变量绑定

    • 创建一个变量x并将数字5赋值给它。
    • x的值改为10,然后打印出来。
    • 尝试创建一个不可变的变量y并尝试更改它的值,观察编译器如何响应。
  2. 函数

    • 编写一个函数add_two,它接受一个i32类型的参数并返回它加上2的结果。
    • 编写一个函数print_greeting,它不接受任何参数并打印出”Hello, Rust!“。
  3. 基本类型

    • 创建一个布尔型变量is_rust_fun并赋值为true
    • 创建一个浮点数pi并赋值为3.14159
    • 创建一个字符型变量favorite_letter并赋值为你最喜欢的字母。
  4. 注释

    • 在你之前写的代码中添加注释,解释每个变量和函数的作用。
    • 尝试使用单行(//)和多行(/* */)注释。
  5. 复合类型

    • 创建一个元组point,包含两个i32类型的值,代表一个二维坐标点。
    • 创建一个数组months,包含一年中所有月份的名称。
  6. 控制流

    • 使用if语句检查一个i32类型的变量是否为正数,负数或零,并打印出相应的信息。
    • 使用loop编写一个无限循环,当循环次数达到10次时使用break退出循环。
    • 使用for循环遍历1100的数字,并只打印出其中的偶数。
  7. 所有权和借用

    • 创建一个字符串变量greeting并赋值为”Hello, world!“。
    • 创建一个函数take_ownership,它获取一个String类型的参数,并打印出来,然后探索函数调用后原变量的状态。
    • 创建一个函数borrow_string,它获取一个对String的不可变引用,并打印出字符串的长度。

Day1 答案

Rust 的基本语法:了解变量绑定、函数、基本类型、注释等。

  1. 变量绑定
    • 创建一个变量x并将数字5赋值给它。
    • x的值改为10,然后打印出来。
    • 尝试创建一个不可变的变量y并尝试更改它的值,观察编译器如何响应。
fn main() {
    // 创建一个可变的变量`x`并初始化为5
    let mut x = 5;
    // 修改变量`x`的值为10
    x = 10;
    // 打印变量`x`的值,预期输出为10
    println!("{}", x);

    // 下面的代码尝试创建一个不可变的变量`y`并更改它的值
    // 但是因为`y`是不可变的,所以这会导致编译错误
    // let y = 20;
    // y = 30; // 这行会导致编译错误,因为不允许改变不可变变量的值
}
  1. 函数
    • 编写一个函数add_two,它接受一个i32类型的参数并返回它加上2的结果。
    • 编写一个函数print_greeting,它不接受任何参数并打印出”Hello, Rust!“。
fn main() {
    // 调用函数`add_two`并打印结果,预期输出为4
    let res = add_two(2);
    println!("{}", res);
    // 调用函数`print_greeting`,预期输出"Hello, Rust!"
    print_greeting();
}

// 定义一个函数`add_two`,接受一个`i32`类型的参数并返回它加上2的结果
fn add_two(num: i32) -> i32 {
    num + 2
}

// 定义一个函数`print_greeting`,它不接受任何参数并打印出"Hello, Rust!"
fn print_greeting() {
    println!("Hello, Rust!");
}
  1. 基本类型
    • 创建一个布尔型变量is_rust_fun并赋值为true
    • 创建一个浮点数pi并赋值为3.14159
    • 创建一个字符型变量favorite_letter并赋值为你最喜欢的字母。
fn main() {
    // 创建一个布尔型变量`is_rust_fun`并赋值为`true`
    let is_rust_fun = true;
    // 创建一个浮点数`pi`并赋值为3.1415926
    let pi = 3.1415926_f64;
    // 创建一个字符型变量`favorite_letter`并赋值为"X"
    let favorite_letter = "X";
}
  1. 注释
    • 在你之前写的代码中添加注释,解释每个变量和函数的作用。
    • 尝试使用单行(//)和多行(/* */)注释。
// 主函数,程序的入口点
fn main() {
    // 调用`add_two`函数,并将结果存储在变量`res`中
    let res = add_two(2);
    // 打印`add_two`函数的结果
    println!("{}", res);
}

// 定义一个函数`add_two`,它接受一个`i32`类型的参数`num`,并返回`num`加2的结果
fn add_two(num: i32) -> i32 {
    num + 2 // 返回输入值加2的结果
}
  1. 复合类型
    • 创建一个元组point,包含两个i32类型的值,代表一个二维坐标点。
    • 创建一个数组months,包含一年中所有月份的名称。
// 主函数,程序的入口点
fn main() {
    // 创建一个元组`point`,包含两个`i32`类型的值,代表一个二维坐标点
    let point = (1_i32, 6_i32);
    // 打印出坐标点
    println!("Point coordinates: ({}, {})", point.0, point.1);

    // 创建一个数组`months`,包含一年中所有月份的名称
    let months = [
        "January", "February", "March", "April", "May", "June",
        "July", "August", "September", "October", "November", "December"
    ];
    // 打印出所有月份
    for month in &months {
        println!("{}", month);
    }
}
  1. 控制流
    • 使用if语句检查一个i32类型的变量是否为正数,负数或零,并打印出相应的信息。
    • 使用loop编写一个无限循环,当循环次数达到 10 次时使用break退出循环。
    • 使用for循环遍历1100的数字,并只打印出其中的偶数。
// 主函数,程序的入口点
fn main() {
    // 定义一个`i32`类型的变量`a`
    let a = 10_i32;
    // 使用`if`语句检查变量`a`的值,并打印出相应的信息
    if a > 0 {
        println!("a is positive");
    } else if a == 0 {
        println!("a is zero");
    } else {
        println!("a is negative");
    }

    // 初始化计数器`b`
    let mut b = 0;
    // 使用`loop`创建一个无限循环
    loop {
        // 当`b`达到10时,使用`break`退出循环
        if b == 10 {
            break;
        }
        // 每次循环,`b`增加1
        b += 1;
    }

    // 使用`for`循环遍历1到100的数字
    for i in 1..=100 {
        // 如果是偶数,则打印出来
        if i % 2 == 0 {
            println!("{}", i);
        }
    }
}
  1. 所有权和借用
    • 创建一个字符串变量greeting并赋值为”Hello, world!“。
    • 创建一个函数take_ownership,它获取一个String类型的参数,并打印出来,然后探索函数调用后原变量的状态。
    • 创建一个函数borrow_string,它获取一个对String的不可变引用,并打印出字符串的长度。
// 主函数,程序的入口点
fn main() {
    // 创建一个`String`类型的变量`greeting`
    let greeting = "Hello, world".to_string();
    // 调用`take_ownership`函数,将`greeting`的所有权传递给函数
    take_ownership(greeting);
    // 下面的代码会导致编译错误,因为`greeting`的所有权已经被移动
    // println!("{}", greeting);
}

// 定义一个函数`take_ownership`,它获取一个`String`类型的参数`s`,并打印出来
fn take_ownership(s: String) {
    println!("{}", s);
    // `s`在这里被丢弃,它所占用的内存被释放
}

// 主函数,程序的入口点
fn main() {
    // 创建一个`String`类型的变量`greeting`
    let greeting = "Hello, world".to_string();
    // 调用`borrow_string`函数,传递一个对`greeting`的不可变引用
    borrow_string(&greeting);
    // `greeting`仍然有效,因为`borrow_string`只是借用了它的值
    println!("{}", greeting);
}

// 定义一个函数`borrow_string`,它获取一个对`String`的不可变引用`s`,并打印出字符串的长度
fn borrow_string(s: &String) {
    println!("The length of '{}' is {}.", s, s.len());
    // `s`的借用在这里结束,但原始字符串`greeting`不受影响
}