0%

Rust-变量、函数与控制流

从 hello world 开始

创建一个 main.rs 文件,写入以下内容

1
2
3
fn main() {
println!("Hello, world!");
}
  • 使用以下命令进行编译运行
1
2
3
rustc main.rs
./main
Hello, world!
  • 也可使用Cargo工具进行构建
1
2
3
4
5
6
cargo new  ${project_name}
cd ${project_name}
#这将会创建如下结构
${project_name}
src/main.rs
Cargo.toml

其中,toml文件内容如下

1
2
3
4
5
6
7
8
[package]
name = "project_name" #项目名称
version = "0.1.0" #项目版本
edition = "2021" #Rust版本,当前为2015 2018 2021,默认为2015

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies] #依赖的包

src/main.rs 中写入内容, 使用cargo build/cargo build --release 进行构建,编译产物将会放在target/debug/${project_name} or target/release/${project_name}下,也可使用cargo run直接编译运行,也可使用cargo check来进行编译检查

变量

  • 变量与可变性
1
2
3
4
let x = 5; //不可变
x = 6; //编译失败
let mut y = 6; //可变
y = 6; //正确的,没问题的
  • 常量
1
const SOME_VALUE: u32 = 1001

常量使用const关键字进行声明,常量可以在任何作用域中声明,并且声明常量时必须注明值的类型

  • 隐藏

当定义多个同名的变量时,只有之前的变量都会被最后一次定义的变量隐藏,直到最后一次定义的变量作用于结束

1
2
3
4
5
6
7
let x = 5;
let x = x + 1;
{
let x = x * 2;
println!("The value of x in the inner scope is: {x}");
}
println!("The value of x is: {x}");

以上代码,首先将x绑定到值5上, 接着通过let x =创建了新的变量x,获取初始值并加一,此时x的值变为6,接着在花括号创建的作用于内,x又被隐藏了一次,此时x的值为12,在作用于结束时,隐藏也结束了,x又变回6,因此以上程序输出为:

1
2
3
4
5
6
$ cargo run
Compiling variables v0.1.0 (file:///projects/variables)
Finished dev [unoptimized + debuginfo] target(s) in 0.31s
Running `target/debug/variables`
The value of x in the inner scope is: 12
The value of x is: 6

隐藏实际上是创建了一个新的同名变量,可以改变变量值类型,并复用这个名字,而mut关键字无法做到这一点,例如

1
2
3
4
5
6
7
let spaces = "   ";
let spaces = spaces.len();
//第一次定义 spaces 为字符串类型,第二次为数字类型,使用隐藏可以做到这一点

let mut spaces = " ";
spaces = spaces.len();
//第一次定义 spaces 为字符串类型,第二句则尝试讲一个数字类型赋值给一个字符串类型的变量,此时编译会失败

数据类型

每一个值都属于某一个数据类型,这将高速Rust编译器它被指定为何种数据,一遍明确数据处理方式,存在两类数据源类型子集:标量复合

通常编译器可以自动推导数据类型,不过某些情况下需要显示的指定数据类型,例如数据类型可能有多种情况时,例如将字符串转为数字

1
let num: u32 = "42".parse().expect("Not a number !");

标量类型

代表单独一个值,Rust中有四种基本标量类型:整形、浮点型、布尔型以及字符串类型

整型

默认类型为i32

长度 有符号 无符号
8-bit i8 u8
16-bit i16 u16
32-bit i32 u32
64-bit i64 u64
128-bit i128 u128
arch isize usize

而对于数字面值,允许使用类型后缀,例如64u8u8类型的64,也允许使用_下划线作为分隔符,例如1_000等价于1000

数字字面值 例子
Decimal (十进制) 98_222
Hex (十六进制) 0xff
Octal (八进制) 0o77
Binary (二进制) 0b1111_0000
Byte (单字节字符)(仅限于u8) b'A'

关于整型溢出,Debug模式下构建的二进制,在发生溢出时会panic,Release模式下构建的二进制会进行二进制补码操作,来绕回最小值,如256+1 = 0

对于整型溢出,可采用以下方式显示处理

  • 所有模式下都可以使用 wrapping_* 方法进行 wrapping,如 wrapping_add
  • 如果 checked_* 方法出现溢出,则返回 None
  • overflowing_* 方法返回值和一个布尔值,表示是否出现溢出
  • saturating_* 方法在值的最小值或最大值处进行饱和处理

浮点型

f32f64,分别占32位于64位,默认为f64,浮点型数据都是有符号的

布尔型

1
2
let t = true;
let f: bool = false; // with explicit type annotation

字符串类型

关键字为char,类型大小为4个字节,并代表了一个Unicode标量值

1
2
3
let c = 'z';
let z: char = 'ℤ'; // with explicit type annotation
let heart_eyed_cat = '😻';

复合类型

可将多个值组合成一个类型,存在两种原生的复合类型:元组与数组

元组类型

将多个其他类型的值组合仅一个复合类型,元组长度固定,一旦声明,其长度不会变化

1
let tup: (i32, f64, u8) = (500, 1.23, 1);

当想从元组中获取单个值时, 可以使用模式匹配来结构元组值,例如

1
2
3
let tup = (500, 1.23, 1);
let (x, y, z) = tup;
//创建了 x, y, z 来将 tup 分成了三个变量,

也可使用.来进行访问,例如tup.0即为第一个值500

不带任何值的元组有个特殊的名称,叫做 单元 元组。这种值以及对应的类型都写作 (),表示空值或空的返回类型。如果表达式不返回任何其他值,则会隐式返回单元值。

数组类型

数组中的每个元素类型必须相同,且数组的长度是固定的,数组在栈上分配空间

1
2
3
4
5
6
let array = [0, 1, 2, 3, 4, 5];
let array2: [i32; 5] = [1, 2, 3, 4, 5]; //5个i32类型数据
let array3 = [3; 5];//标识数组中有 5 个 3

let one = array[1];
println!("number is {one}");

函数与控制流

函数

使用关键字fun声明,函数名与变量名使用Unix风格,小写字母,并使用下换线分割单词

1
2
3
4
5
6
7
8
fn main() {
println!("Hello, world!");
another_function();
}

fn another_function() {
println!("Another function.");
}

Rust 不关心函数定义所在的位置,只要函数被调用时出现在调用之处可见的作用域内就行。

函数的参数

函数的参数必须声明参数类型,多个参数使用逗号分割

1
2
3
4
5
6
fn main() {
another_function(5);
}
fn another_function(x: i32) {
println!("The value of x is: {x}");
}

语句和表达式

  • 语句 执行一些操作但不返回值的指令,以分号结尾
  • 表达式 计算并产生一个值
1
2
let a = 1; //语句不会返回任何值
let b = (let c = 2);//编译会失败 let c = 2 不会返回值

在rust中,函数调用是一个表达式。宏调用是一个表达式。用大括号创建的一个新的块作用域也是一个表达式

1
2
3
4
5
let a = {
let b = 1;
b + 1
}
// 此时 a = 4

具有返回值的函数

1
2
3
4
5
6
7
fn ret() -> i32 {
2
}

fn main() {
let x = ret();
}

控制流

if表达式

if表达式使用的值必须为bool类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
let num = 3;
if num < 5 {
//...
} else {
//...
}

if num { //不能这样使用

}

if num == 1 {
//...
} else if num == 2 {
//...
} else {
//...
}

if也可在let中使用,但是需要每个分支都必须是相同的类型

1
2
3
let flag = true;
let num = if flag {0} else {1}; //可以
let num2 = if flag {0} else {"one"} //不行

循环

loop循环

无条件的循环,可以使用break从循环跳出,同时可以返回一个值,也可以使用continue跳过循环

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

let mut cnt = 0;
let res = loop {
cnt += 1;
if cnt == 100 {
break cnt * 2;
}
}
//res = 200

breakcontinue仅能控制当前的循环,对于嵌套的循环,可以配合标签一起使用来控制循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let mut num = 0;
'loop_start': loop {
println!("cnt = {cnt}");
let mut remaining = 10;
loop {
println!("remaining = {remaining}");
if remaining == 9 {
break;
}
if cnt == 2 {
break 'loop_start';
}
remaining -= 1;
}
cnt += 1;
}
println!("End cnt = {cnt}");

while条件循环

1
2
3
4
5
6
7
8
9
10
11
let mut num = 3;
while num != 0 {
num -= 1;
}

let a = [10, 20, 30, 40, 50];
let mut index = 0;
while index < 5 {
println!("the value is: {}", a[index]);
index += 1;
}

for遍历集合

1
2
3
4
5
let a = [10, 20, 30, 40, 50];

for one in a {
println!("the value is: {one}");
}
-------------本文结束感谢您的阅读-------------