Back
Featured image of post Rust 学习笔记02 - 基本语法

Rust 学习笔记02 - 基本语法

导航页

变量声明

Rust 使用 let 关键字声明变量,和其他新兴语言一样,默认情况下是 Immutable 的。需要使用 mut 关键字使其可变。

fn main() {  let x = 5;  println!("The value of x: {}", x);  let mut y = 5;  println!("The value of y: {}", y); }

请注意,这里虽然没有写类型,但不代表 Rust 是动态的。这是类型推断,相当于编译器自动帮你写成了 let x: i32 = 5;

基本类型和 &str(字符串自变量) 可以被声明为编译时常量:

const MAX_POINTS: u32 = 100_000; // compile-time constant  fn main() {  const STRING: &str = "123123";  println!("The value of MAX_POINTS: {}", MAX_POINTS); }

常量必须显式写明类型。同时 top-level 或局部声明都是可以的。

Shadow

虽然不允许修改变量,但 Rust 允许 Shadow 一个变量的名称。比如:

let a = 1; let a = 100;

然而,我不建议这种做法,很多语言也不建议这种做法。建议读者自行斟酌。

注释

// 单行注释  /*  * 多行注释  * /*  * * 可以嵌套  * */  */  /// /// 文档注释 ///

基本类型

基本类型(primitives),也叫原生类型、原语。是组成所有数据结构的基本元素。

整数类型

长度Signed / 有符号Unsigned / 无符号
8 位i8u8
16 位i16u16
32 位i32u32
64 位i64u64
128 位i128u128
架构特定isizeusize

Rust 的基本类型关键字非常简洁易懂,直接就是类型缩写 + 所占空间,不必纠结 int long long long long int short 的意义。

同时少有的支持 128 位数据类型。

isizeusize 即根据平台使用不同的长度,如 32 位架构上,就占 32 位 ,最常用来存数组下标。

Rust 也支持不同进制的整数字面量:

字面量示例
十进制98_222
十六进制0xFF
八进制0o77
二进制0b1111_000
字节(仅 u8b'A'

可以使用 _ 分隔数字,便于阅读。

浮点类型

Rust 的浮点数遵循 IEEE 754 标准。具有 f32f64 两种长度可选。默认为 f64

浮点数可以省略小数部分,如 0.,但不可省略点号和整数部分。

字符

关键词为 char,字面量使用单引号包围,可存任意 Unicode 定义的字符,占 4 字节。

let a = 'a' let alpha = 'α' let emoji = '😄' // 大多数 emoji 都可以存为字符

布尔

关键词为 bool,字面量 truefalse,占 1 字节。

Unit 类型

空元组:()。尽管是元组,但它是一种特殊的类型,而不是复合类型,因为不包含多个值。

复合类型

  • 数组:[1, 2, 3]

    • Rust 的数组索引和多数 C-like 语言一样,从 0 开始
    • 数组的长度是固定的,所以类型注解应写成 let arr: [i32; 5] = [1,2,3,4,5]
    • 多个同样的值可以使用 [0; 5] 这种语法,相当于 [0, 0, 0, 0, 0,]
    • 在编译时,Rust 能够检测部分数组越界。如 let a = [1,2,3]; a[10]; 会直接不过编译。
    • 在运行时,若数组越界了,将会直接 panic 退出程序,而不是继续执行。
  • 元组:(1, true)

    • 类型注解类似这样:(i32, bool)

    • 当元素多于 3 个时,建议使用结构体定义。

    • 可以直接访问对应的类型: x.0 x.1

    • 解构声明:let (x, y, z) = (500, 6.4, 1);

数组和元组的区别在于:数组只能存一种类型;而元组能存多种类型。

字面量后缀

可以使用对应关键字当作类型后缀。例如:

let a = 100i64; let b = 100i32; let c = 100i8;  let d = 10.123f32; let f = 10.112312312f64;

操作符

这部分和大多数类 C 语言一样。就不列出全表了。

值得一提的是,Rust 类似很多新语言(如 Swift),没有 ++ 操作符,避免了各种魔怔写法。建议使用 value += 1 替代。

优先级不明晰的时候,建议括号包裹即可,不用背优先级。

函数

函数使用 fn 声明,函数名使用 snake_case 风格:

fn function() { // 隐式返回 Unit 类型 ()  println!("Hello Function!");  another_function(); }  fn another_function() {  println!("Another function."); }

参数可以这样写:

fn main() {  another_function(5, 'g'); }  fn another_function(x: i32, lable: char) {  println!("The value of x is: {}{}", x, lable); }

Rust 刻意不支持默认参数,也不支持函数重载。

Rust 允许将不写分号的语句直接返回,而不用写 return

fn return_no_semicolon() -> i32 {  123123 }

然而我认为这无疑是一种垃圾设计,舍本逐末。为了这里一点的方便,让其他所有地方都要写分号。

况且为何不直接:

fn return_no_semicolon() -> i32 = 123123

控制流

条件

if-else

这里用到了 rand 库,编辑 Cargo.toml

[dependencies] rand = "0.8.0"
use rand::{thread_rng, Rng}; // 导包  fn main() {  // 这里的 ..= 是闭区间, 也就是说包括 1 和 100  // 与之对应的有 .. 是左闭右  let num = thread_rng().gen_range(1..=100);  let cmp_num = thread_rng().gen_range(1..=100);  if num < cmp_num {  println!("{num} is greater than {cmp_num}!")  } else {  println!("{num} is not greater than {cmp_num}!")  } }

和其他语言类似,Rust 可以省略包裹条件的括号,但不能省略大括号。

Rust 的条件必须是布尔类型,以下代码会报错:

if 1 {}

if-else-if

// 后面将会省略 main 函数和生成 num 的代码 if num % 3 == 0 {  println!("{num} is is divisible by 3!") } else if num % 4 == 0 {  println!("{num} is is divisible by 4!") } else if num % 5 == 0 {  println!("{num} is is divisible by 5!") } else {  println!("{num} is not divisible by 3, 4, 5.") }

match

match num {  1 => println!("1"),  2 => println!("2"),  _ => println!("Other number"), }

match 要求分支必须是穷尽的,常常用来处理枚举。后面讲到枚举的时候再详细介绍。

作为表达式

穷尽的 ifmatch 允许做看作表达式,用于赋值。

fn main() {  let condition = true;  let number = if condition { 5 } else { 6 };   println!("The value of number is: {}", number); }

循环

while

let mut count = 0;  while count < 5 {  count += 1;  println!("Again! {count}"); }

loop

死循环,类似于 while true {},但对 loop 有着不同的语义。Rust 建议任何能用 while true {} 的地方都用 loop

break & continue

break 用于跳出整个循环。continue 用于跳过本次循环。

fn main() {  let mut count = 0;   while count < 5 {  count += 1;  print!("{count} ");  if count == 1 {  continue;  }  if count == 3 {  break;  }  } }

label

如果存在嵌套循环,可以使用循环标签,跳过指定的循环,而不是最内层循环。

fn main() {  let mut count = 0;  'counting_up: loop {  println!("count = {}", count);  let mut remaining = 10;   loop {  println!("remaining = {}", remaining);  if remaining == 9 {  break;  }  if count == 2 {  break 'counting_up;  }  remaining -= 1;  }   count += 1;  }  println!("End count = {}", count); }  /* Output: count = 0 remaining = 10 remaining = 9 count = 1 remaining = 10 remaining = 9 count = 2 remaining = 10 End count = 2 */

for

for count in 1..=5 {  println!("Again! {count}"); }  // rev() 用于逆序, 此处会从 5 倒数到 1 for count in (1..=5).rev() {  println!("Again! {count}"); }

foreach

let a = [10, 20, 30, 40, 50]; for element in a.iter() {  println!("The value is {element}"); }