本文的具体代码在这里

通过一个猜数字游戏掌握 rust 的基础语法

1
cargo new guess && cd guess

定义用户输入的数字变量

1
2
3
4
5
6
7
8
9
10
11
fn main() {
println!("guess the number!");

// rust 定义的变量是不可变的,如果要定义可变, 必须要加上 mut 关键字
let mut guess = String::new();

// 传参中 & 表示引用
io::stdin().read_line(&mut guess)
.expect("Failed to read line");
let guess:u32 = guess.trim().parse();
}

rust 是函数式编程语言, 大多数变量在函数式编程中, 是变量不可变的, 因为在函数中变量传入过程中, 确保变量的不可变性, 是函数式编程的一大特性. 所以 rust 的变量默认是不可变的, 反其道而行, 如果要将其设置为可变, 则要加上 mut 关键字

判断用户输入合法性 match

上面用户的输入有可能出现非数字的情况, 所以需要判断用户输入合法性,使用 match 语句, match 是函数式编程语言特有的语法

match 语法很强大, 我们先暂时把 Err 的情况统一处理为 0

1
2
3
4
5
6
7
let guess: u32  = match guess.trim().parse() {
Ok(num) => num,
Err(_) => {
println!("Please type a number!");
0
}
};

处理随机数

  • 我们生成一个随机数, 利用第三方的 rand
    • Cargo.toml 的依赖里添加: rand = "^0.8.5"
    • 再在命令行运行 cargo build 命令, 自动安装依赖
1
2
[dependencies]
rand = "^0.8.5"
  • 并将其他代码包裹在 loop 循环里, 相当于其他语言的 whlie (true) {}
  • 将上面 Err() 的逻辑改为 continue, 如果用户猜错, 则继续猜, 不会跳出循环
1
2
3
4
5
6
7
8
9
10
11
use rand::Rng;

fn main() {

// .gen_range(1..=100) 等价于 .gen_range(1..101)
let secret_number = rand::thread_rng().gen_range(1..=100);
loop {

// 其他代码....
}
}

比较数字的大小

引入 cmpOrdering 库, 将上面随机生成的 secret_number 和用户输入的 guess 进行比较

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
use std::cmp::Ordering;

fn main() {

// 其他代码....

loop {
// 其他代码....

// 这里要传入引用的指针&
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too big!"),
Ordering::Equal => {
println!("You win!");
// 猜对则跳出循环:
break;
}
}
}
}

最后, 贴出完整代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
use std::io;
use rand::Rng;
use std::cmp::Ordering;

fn main() {
println!("guess the number!");

let secret_number = rand::thread_rng().gen_range(1..=100);

loop {
let mut guess = String::new();
io::stdin().read_line(&mut guess)
.expect("Failed to read line");

let guess: u32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => continue
};

// 等价于 println!("guess: {}", guess);
println!("guess: {guess}");

match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too big!"),
Ordering::Equal => {
println!("You win!");
break;
}
}
}
}

编程基础概念在 Rust 中的体现

变量

  • 定义变量后,如果赋值后要改变,必须加上 mut 关键字,否则则不能改变其值。同样的,定义了 const 之后的常量,则不能加上 mut

  • 数字较大时,可以用下划线替代千分号分隔数字

1
2
3
4
5
6
7
8
// variables and constance
let mut x:i32 = 5;
println!("x value is {x}");
x = 6;
println!("x value is changed: {x}");

const COUNT: u32 = 1_000_000;
println!("COUNT value is {COUNT}");
  • 变量遮蔽(variable shadowing),同一个作用域内,允许对同一变量名进行二次定义
  • 这样做的好处是方便,坏处是后期代码量大会使得代码难以维护,所以使用时应谨慎
1
2
3
4
5
// variable shadowing:
let y: u32 = 7;
println!("y value is {}", y);
let y: &str = "eight";
println!("y value is changed: {}", y);

数据类型

Intergers

分为有符号(i)和无符号(u):

Length Signed Unsigned
8-bit i8 u8
16-bit i16 u16
32-bit i32 u32
64-bit i64 u64
128-bit i128 u128
arch isize usize
  • 其取值范围分别是:2 的 8、16、32、64、128 次方
  • Signed的数由于有负数的取值范围,所以比 Unsigned 取值范围减半
Type Range
i8 -128~127
u8 0~255
i16 -32768~32767
u16 0~65535
i32 -2147483648~2147483647
u32 0~4294967295
i64 -9223372036854775808~9223372036854775807
u64 0~18446744073709551615
i128 -170141183460469231731687303715884105728~170141183460469231731687303715884105727
u128 0~340282366920938463463374607431768211455

进制:
| Number literals | Example |
| :–: | :–: |
| Decimal | 98_222 |
| Hex | 0xff |
| Octal | 0o77 |
| Binary | 0b1111_0000 |
| Byte (u8 only) | b’A’ |

Floating

1
2
let x1 = 2.45; // f64
println!("{}", x1);

Boolean

1
2
let t = false;
println!("{}", t);

Charactor (字符型)

1
2
3
4
5
6
let ch = 'z';
println!("char z: {}", ch); // char z: z
let z_char: char = 'ℤ'; // with explicit type annotation
println!("char z: {}", z_char); // char z: ℤ
let heart_eyed_cat = '😻';
println!("heart_eyed_cat: {}", heart_eyed_cat); // heart_eyed_cat: 😻

tuple (元组)

1
let tup0 = (11,22);
1
2
3
4
5
6
7
8
9
// 可对元组里的每个元素单独定义:
let tup1: (&str, i32, f32) = ("let's get Rusty!", 1_000_000, 0.45);
// 解构:
let (channel, sub_count, float_num) = tup;
println!("{} {} {}", channel, sub_count, float_num); // let's get Rusty! 1000000 0.45

// 索引:
let sub_count: i32 = tup.1;
println!("sub_count: {}", sub_count);

Array

1
2
3
4
let arr = [1, 2, 3, 4, 5];

// 定义类型:[数据类型,数组长度]
let arr1: [i32; 5] = [1, 2, 3, 4, 5];

Funtion

  • 定义与调用:
1
2
3
4
5
6
7
fn main() {
my_function(12, 34);
}

fn my_function (x: i32, y: i32) -> i32 {
println!("my function: {}, y: {}", x, y);
}
  • 函数返回值,可省 return 关键字,且返回的语句或变量无需加 ;
  • 函数如有返回值,则须用 -> 定义返回值类型
1
2
3
4
5
6
7
8
fn main() {
let result = my_function(12, 34);
println!("result: {}", result);
}

fn my_function (x: i32, y: i32) -> i32 {
x + y
}

Control Flow

if-else

  • if-else分支的条件无需套括号 ()
1
2
3
4
5
6
7
8
let number: i32 = 5;
if number < 10 {
println!("1 true");
}else if number < 22 {
println!("2 true");
}else{
println!("false");
}
  • if-else 条件可写在一行:
1
2
let condition: bool = true;
let num:i32 = if condition { 1 } else { 2 };

while

1
2
3
4
5
6
7
8
let mut n = 3;
while n != 0 {
println!("{}!", n);
n -= 1;
}
// 3!
// 2!
// 1!

loop

和 while 循环有点类似:

1
2
3
4
5
6
7
8
9
10
11
12
13
loop {
println!("again!");
break
}

let mut counter = 0;
let loop_result = loop {
counter += 1;
if counter == 10 {
break counter;
}
};
println!("loop_result: {}", counter); // loop_result: 10

for

1
2
3
4
5
6
7
let arr_for = [11,22,33,44,55];
for item in arr_for.iter() {
println!("the arr value is: {}", item);
}
// the arr value is: 11
// the arr value is: 22
// the arr value is: 33
  • 类似 pythonrangescalaRange ,在rust里使用.. 表示数值范围:
  • .. 的取值范围包括开始值,不包括终止值:
1
2
3
4
5
6
for value in 1..4 {
println!("{}!!", value);
}
// 1!!
// 2!!
// 3!!