Rust 学习笔记
前情摘要
使用 Rust 完成的 Linux 代码的编写将要合入 Linux 主分支的事情激励我去学习 Rust 这门语言。个人感觉 Rust 会是未来 Kernel 乃至 System 的趋势,通过使用 Cargo 进行版本管理可以有效缓解 C 语言的版本问题,同时依赖模块类似于 Go 以及 Python 的第三方库下载可以有效解决 C 的死板的头文件以及依赖库问题,这些都是 Rust 的优势所在。更重要的是,通过引入生命周期的概念使得在运行时并不需要 GC 的这种我愿意称之为“语言归约”的设计,在运行性能上必然会优于 Java 和 Python,在内存泄漏问题上优于 C++,这也是我决心学习 Rust 的原因。
Rust 的学习方式比较多,我选择通过 Rustlings 这种类似于游戏的方式来学习 Rust 的各种知识。
我将我的解答开源在 GitHub 上,欢迎大家学习与围观。
Rustlings
Intro
Intro.1
这个问题就是展示一下 Rustlings 的图标,同时展示最简单的输出宏 println!,就和 C 语言的 printf 一样,是我们学习的基础。
Intro.2
这个问题希望我们输出 “Hello World”,一个点就是在 Rust 中 println! 格式串中的 {} 将会被替换成任意参数,结果如下所示:
println!("Hello {}!", "World");
Variables
Variables.1
声明一个变量,需要加 let,如果不声明 mut 则默认为不可变变量。
let x = 5;
println!("x has the value {}", x);
Variables.2
这里变量 x 需要进行初始化,注意初始化的时候需要变量类型和变量初值。
let x: i32 = 10;
if x == 10 {
println!("x is ten!");
} else {
println!("x is not ten!");
}
注意 Rust 的变量初始化与 C 语言并不同,格式类似于变量:类型=初值。
Variables.3
在这里变量 x 并没有初始化,Rust 编译器并不允许这种未赋值就使用的行为。
let x: i32 = 100;
println!("Number {}", x);
Variables.4
Rust 里面如果希望改变一个变量的值,需要定义为 mut 类型。
let mut x = 3;
println!("Number {}", x);
x = 5; // don't change this line
println!("Number {}", x);
Variables.5
在 Rust 中存在影子变量这中说法,这在 C 语言中是不允许的(会触发编译错误)。
实际上,在使用影子变量之后,编译器将仅仅只能看到后面的变量。
let number = "T-H-R-E-E"; // don't change this line
println!("Spell a Number : {}", number);
let number = 3; // don't rename this variable
println!("Number plus two is : {}", number + 2);
注意使用影子变量的方法是需要用 let
关键字。
Variables.6
实际上这里是 C 语言的写法,需要使用 Rust 自己的带类型赋值方法。
const NUMBER:i32 = 3;
Functions
Functions.1
这里实际上是对于函数 call_me
,有使用但是没有声明,因此我们只需要声明一下即可。
fn call_me() {
}
Functions.2
这里函数的形参没有指定类型,需要指定类型
fn call_me(num: i32) {
for i in 0..num {
println!("Ring! Call number {}", i + 1);
}
}
Functions.3
函数形参和实参并不匹配,这里需要在主函数加上实参。
call_me(10);
Functions.4
Rust 在返回值设置上,并不像 C 语言那种使用 Return Statement 来标识返回值。而是通过最后一行是否使用分号决定返回的内容。同时在函数实现时需要使用 -> type 来标注函数的返回值。
此处需要补充的就是函数的返回值类型:
fn sale_price(price: i32) -> i32{
if is_even(price) {
price - 10
} else {
price - 3
}
}
Functions.5
函数如果需要返回,则并不需要加分号。
fn square(num: i32) -> i32 {
num * num
}
If
If.1
这里需要实现一个比大小的函数,注意 Rust 语法和 C 语法不一样的一点,Rust 的 If statement 部分并不需要加括号:
pub fn bigger(a: i32, b: i32) -> i32 {
// Complete this function to return the bigger number!
// Do not use:
// - another function call
// - additional variables
if a > b {
a
} else {
b
}
}
If.2
这个需要根据下面的测试程序决定没一个输入应该输出什么字符串。
pub fn foo_if_fizz(fizzish: &str) -> &str {
if fizzish == "fizz" {
"foo"
} else if fizzish == "fuzz" {
"bar"
} else {
"baz"
}
}
Quiz1
这个小测试实际上需要我们读懂上面的英文含义,并实现calculate_price_of_apples
函数。
按照所叙述的要求,当购买数量超过 40 时,每个苹果 1 元,否则 2 元。
fn calculate_price_of_apples(cnt: i32) -> i32 {
if cnt <= 40 {
cnt << 1
} else {
cnt
}
}
Primitive_types
Primitive_types.1
可以发现下面的变量 is_evening 有使用没有定义,再考虑到这里代码的语义,可以得到需要填写的代码:
let is_evening = false; // Finish the rest of this line like the example! Or make it be false!
Primitive_types.2
这个是一个开放性问题,只需要对于 your_character 这个变量进行定义即可:
let your_character = '3';
Primitive_types.3
这里我们需要定义数组,方式如下所示:
let a = ["qaq"; 666];
这句话的含义为定义一个字符串数组,数组里面每一个元素均为 “qaq”,数组总长度为 666.
Primitive_types.4
这里实际上是 Rust 切片的应用。说到切片,就不得不提到所有权这个概念,实际上 Rust 对于地址的所有权管理是十分严格的。切片的变量实际上并不能获得地址的所有权的,仅仅是一个引用。
根据下面的测试程序,我们可以得到下面的代码:
let nice_slice = &a[1