15天100题入门Rust——Day1
在各种编程语言的学习中,”反复入门”是许多学习者的共同难题,尤其是对于Rust这样的系统编程语言。Rust以其独特的所有权模型、零成本抽象和内存安全特性著称,但这些也使得它的学习曲线相对陡峭。因此,很多开发者在初次尝试后,可能会发现自己不得不重新开始学习Rust。
为了解决这个问题,我设计了一个15天的Rust学习计划,旨在通过15个精心挑选的主题,带领大家逐步深入Rust的世界。每天,我们将专注于一个核心主题,并通过一系列实践任务来巩固学习成果。每个任务都会有相应的答案和详细解释,即使在遇到挑战时,你也能够找到方向和答案。
今天,我们将从Rust的基础语法开始,这是任何Rust项目的基石。我们会一起探索变量绑定、函数、基本类型、注释等概念,并通过实际的编码练习来加深理解。这些基础知识将为接下来的学习打下坚实的基础,让你在接下来的14天里,能够更加自信地探索Rust的高级特性。
下面是这15天的内容大纲:
Rust的基本语法:了解变量绑定、函数、基本类型、注释等。
所有权(Ownership):这是Rust最核心的概念之一,包括所有权规则、借用(borrowing)、生命周期(lifetimes)。
类型系统:理解不可变性(immutability)、结构体(structs)、枚举(enums)、泛型(generics)和特征(traits)。
错误处理:学习使用
Result和Option枚举来处理可能的错误。模式匹配:掌握如何使用
match和if let来进行模式匹配。集合类型:熟悉Rust的标准集合类型,如向量(Vec)、字符串(String)、哈希映射(HashMap)等。
迭代器:理解迭代器(iterators)的使用和创建,以及它们如何与闭包(closures)配合使用。
并发编程:学习Rust的线程(threads)、消息传递(message passing)、共享状态(shared state)、以及
async/.await。模块系统:了解如何组织代码和模块,使用
mod和use声明。包管理和Crates.io:学习如何使用Cargo进行包管理,以及如何在Crates.io上发布库。
宏:了解宏(macros)的基础知识,它们如何用于代码生成。
FFI(Foreign Function Interface):学习如何使用Rust与其他语言编写的代码交互。
测试:掌握如何编写测试来验证你的代码。
文档:学习如何为你的代码编写文档注释。
Rust的生态系统:熟悉一些流行的Rust库和框架,以及如何在项目中使用它们。
Day1 问题
Rust的基本语法:了解变量绑定、函数、基本类型、注释等。
变量绑定:
- 创建一个变量
x并将数字5赋值给它。 - 将
x的值改为10,然后打印出来。 - 尝试创建一个不可变的变量
y并尝试更改它的值,观察编译器如何响应。
- 创建一个变量
函数:
- 编写一个函数
add_two,它接受一个i32类型的参数并返回它加上2的结果。 - 编写一个函数
print_greeting,它不接受任何参数并打印出”Hello, Rust!“。
- 编写一个函数
基本类型:
- 创建一个布尔型变量
is_rust_fun并赋值为true。 - 创建一个浮点数
pi并赋值为3.14159。 - 创建一个字符型变量
favorite_letter并赋值为你最喜欢的字母。
- 创建一个布尔型变量
注释:
- 在你之前写的代码中添加注释,解释每个变量和函数的作用。
- 尝试使用单行(
//)和多行(/* */)注释。
复合类型:
- 创建一个元组
point,包含两个i32类型的值,代表一个二维坐标点。 - 创建一个数组
months,包含一年中所有月份的名称。
- 创建一个元组
控制流:
- 使用
if语句检查一个i32类型的变量是否为正数,负数或零,并打印出相应的信息。 - 使用
loop编写一个无限循环,当循环次数达到10次时使用break退出循环。 - 使用
for循环遍历1到100的数字,并只打印出其中的偶数。
- 使用
所有权和借用:
- 创建一个字符串变量
greeting并赋值为”Hello, world!“。 - 创建一个函数
take_ownership,它获取一个String类型的参数,并打印出来,然后探索函数调用后原变量的状态。 - 创建一个函数
borrow_string,它获取一个对String的不可变引用,并打印出字符串的长度。
- 创建一个字符串变量
Day1 答案
Rust 的基本语法:了解变量绑定、函数、基本类型、注释等。
- 变量绑定:
- 创建一个变量
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; // 这行会导致编译错误,因为不允许改变不可变变量的值
}
- 函数:
- 编写一个函数
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!");
}
- 基本类型:
- 创建一个布尔型变量
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";
}
- 注释:
- 在你之前写的代码中添加注释,解释每个变量和函数的作用。
- 尝试使用单行(
//)和多行(/* */)注释。
// 主函数,程序的入口点
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的结果
}
- 复合类型:
- 创建一个元组
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);
}
}
- 控制流:
- 使用
if语句检查一个i32类型的变量是否为正数,负数或零,并打印出相应的信息。 - 使用
loop编写一个无限循环,当循环次数达到 10 次时使用break退出循环。 - 使用
for循环遍历1到100的数字,并只打印出其中的偶数。
- 使用
// 主函数,程序的入口点
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);
}
}
}
- 所有权和借用:
- 创建一个字符串变量
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`不受影响
}