The Rust Programming Language
Rust 编程语言笔记。
安装
使用 rustup 来安装 Rust。
Rust 源文件以 .rs 作为扩展名。
几个常用的命令:
- 编译:rustc .rs-file
- 运行:./compiled-file
- 检查 Rust编译器版本:rustc --version
- 检查 rustup版本:rustup --version
- 更新 Rust编译器:rustup update
- 卸载 Rust和rustup:rustup self uninstall
- 本地文档:rustup doc
包管理工具
Rust 的包管理工具是 Cargo。
几个常用的命令:
- 检查 cargo的版本:cargo --version
- 新建项目:cargo new
- 项目构建:cargo build
- 运行项目:cargo run
- 项目检查:cargo check:该命令确保项目可以编译,但不生成可执行文件,速度比cargo build更快
包
Rust 使用 TOML(Tom's Obvious Minimal Language) 格式来管理依赖。
使用 cargo new 创建新项目后,在该项目的目录下会有名为 Cargo.toml 的文件来管理依赖和包。
可以使用 cargo add 来添加依赖,也可以在 .toml 文件的 [dependencies] 字段下添加包名。
例如:
[dependencies]
rand = "0.8.5"包的版本使用三位的标准包标准。
在 .rs 文件中使用关键字 use 来导入包。
规范
- Rust的缩进采用的是 4 个- space,而不是- tab
- Rust使用 snake case 对变量和函数命名,也就是全部使用小写字母,单词中间使用下划线- _连接,例如:- var_name
Hello World
fn main() {
    println!("Hello, World!");
}- fn是函数(function)的关键词
- main()是 Rust 的主函数、类似于 C、C++
- 每行结束需要用分号 ;表示
注释
Rust 有三种注释:
- 单行注释: //
- 多行注释:/* */
- 文档(DocString)注释:/** */
变量
Rust 是静态类型语言,在声明变量时需要使用关键词 let 并在冒号 : 后指明变量的类型。这点类似于 Python 的 Type-Hint 以及 TypeScript。
let var: u8 = 0;可修改性
Rust 中的所有变量都有可修改(mutable)或不可修改性(immutable),如果在声明变量时不明确指明,那么默认为不可修改。
使用关键字 mut 表示变量可以修改。
let mut var: u8 = 2;
u8 = 3; // That's OK
let ano_var: u8 = 2;
u8 = 3; // Errorshadowing
fn main() {
    let x: u32 = 5;
  	let x = x + 1;
	  {
    	let x = x * 2;
	    println!("The inner x: {x}");
  	}
	  println!("The outer x: {x}");
}
// The inner x: 12
// The outer x: 6如上述代码所示:声明了多个名为 x 的变量,在 Rust 中,我们称 第一个 x 被第二个 x shadow,或者 第二个 x overshadow 了第一个 x,这意味着:当使用 x 这个变量时,编译器总是使用第二个 x,即第二个 x 拿走了第一个 x 的所有,直到程序流离开第二个 x 的作用域或者第二个 x 也被 shadow。
shadow 和 mut 的区别
- shadow 使得可以改变同名变量的类型,但是 mut会在编译时出错
fn main() {
    let x: &str = "     ";
	  let x: usize = x.len(); // 5
  
    let mut x: &str = "     ";
    x = x.len()             // Error
}- shadow 使得可以修改不可修改的同名变量的值
fn main() {
    let x: u32 = 5;
    x = 6;             // Error, 因为没有变量默认是不可修改的
    
    let x: u32 = 5;
    let x = 6;
  	println!("{}", x)  // 6
}常量
Rust 使用 const 关键字声明常量。通常来说,需要用大写字母声明常量。
const THE_DAY_I_START_LEARNING_RUST: String = "2023.5.9";不可修改的变量和常量的区别在于:
- 常量只能是不可修改的,不能通过 mut关键字使得常亮可以修改
- 常量通常只赋值给常量声明的表达式,而不是运行时的计算表达式
- 常量可以在任何作用域(scope) 中声明,在程序运行期间,在其声明的作用域中是有效的
数据类型
Rust 是静态类型语言,因此在编译时就需要知道所有的变量类型。
例如在数据转换中:
fn main() {
    let var = "100".parse().expect("Not a number!"); // Error
    let var: u32 = "100".parse().expect("Not a number"); // OK, var == 100
}基本类型
Rust 的基本数据类型有 4 种:
- 布尔 bool
- 整型 integer
- 浮点型 float
- 字符型 char
布尔型
布尔型有两个值:
- true
- false
整型
整型有 6 种长度,每种长度分类有符号和无符号两类。
| 长度 | 有符号 | 无符号 | 
| 8-bit | i8 | u8 | 
| 16-bit | i16 | u16 | 
| 32-bit | i32 | u32 | 
| 64-bit | i64 | u64 | 
| 128-bit | i128 | u128 | 
| arch | isize | usize | 
整型字面量(literal)
| 字面量 | 举例 | 
| 十进制 | 98_222 | 
| 十六进制 | 0xff | 
| 八进制 | 0o77 | 
| 二进制 | 0b1111_0000 | 
| 字节(u8) | b'A' | 
- 整型的默认类型为 i32
- 在使用集合类数据类型的索引时,使用 usize或isize
浮点型
浮点型有两类,而且都是有符号的:
- f32
- f64:默认类型
字符型
字符型用 c (character)标识。
复合类型
Rust 的复合类型包括:
- 元组
- 数组
元组
元组 tuple:用括号 () 声明,元组元素的数据类型可以不同。
元组是定长的,一旦声明后就不能改变大小。
let tup: (u32, f32, bool) = (1, 2.2, true);和 Python 不同,Rust 中的元组可以使用 . 运算符来访问。
let tup: (u32, f32, bool) = (1, 2.2, true);
println!("{}", tup.0); // 1
println!("{}", tup.2); // 2.2
println!("{}", tup.3); // true数组
数组 array: 用方括号 [] 声明,数组元素的数据类型必须相同。
数组也是定长的。
let arr: [u8; 5] = [1, 2, 3, 4, 5];; 的前后分别表示数组元素的类型和数组元素的个数。
可以使用索引 [],访问数组元素,因为数组以定长形式存储在栈中:
let first = arr[0];控制
条件
用 if, else if, else 等关键字控制程序流。
Rust 的条件语句不需要外嵌圆括号 (),但是也不同于 Python 的 :,条件执行体需要用花括号:{}。
fn main() {
  let var: u32 = 8;
  if var > 0 {
    println!("{var} is greater than 0!");
  } else if var < 0 {
    println!("{var} is less than 0!");
  } else {
    println!("{var} is equal to 0!");
  }
}注:和 JavaScript、Ruby 等语言不同,Rust 语言不会在条件判断时自动转换变量的类型,例如:
fn main() {
	let var: bool = true;
  if var == 0 {
    println!("You can't get there in Rust");
  }
}循环
Rust 中有 3 种循环:
- 无限循环 loop
- while循环
- for循环
结束、跳出循环的关键字:
- break跳出当前循环
- continue结束本次循环(开始下一次循环)
loop 循环
Rust 可以使用关键字 loop 来创建无限循环,例如:
loop {
  println!("This is a infinite loop");
}
// 等价于
while true {
  println!("This is a infinite loop");
}continue 对 loop 循环无效。
可以用 ``name给loop` 循环命名(标识符)以跳出多层循环,例如:
fn main() {
  let mut count: u32 = 10;
	`outer: loop {
    let mut remaining: u32 = 2;
    loop {
      println!("{}", count * remaining);
      remaining -= 1;
      if remaining == 0 {
        break outer;
      }
    }
    count -= 1;
    println!("{}", count);
    if count == 0 {
      break;
    }
  }
}while 循环
类似于其他语言的 while 循环。
同样,Rust 的循环条件语句不需要外嵌圆括号 (),但是也不同于 Python 的 :,循环体需要用花括号:{}。
fn main() {
  let mut count = 10;
  while count > 0 {
    println!("{}", count);
    count -= 1;
  }
}for 循环
for in
for in 循环对遍历对象中的每一个元素都执行遍历:
fn main() {
  let arr: [u32; 5] = [1, 2, 3, 4, 5];
  for element in arr {
    println!("{}", element);
  }
}for range
for range 类似于 Python 的同名函数。
fn main() {
  for i in 0..5 {
    println!("{}", i);
  }
}使用 .rev() 倒序输出:
fn main() {
  for i in (0..5).rev() {
    println!("{}", i);
  }
}函数
使用关键字 fn 声明函数,Rust 总是首先执行 main 函数。
fn main() {
  let var: u32 = 3;
  increment_and_print(var);
}
fn increment_print(num: u32) -> u32 {
	num += 1;
  println!("{}", num);
  return num;
  // num
}函数的参数需要声明名称和类型,返回值用 -> 表示,只需要声明类型。
可以使用关键字 return 显式返回值,也可以把返回值写在最后一行(不要使用 ; 结尾)。Rust 默认把最后一行的值作为返回值。
输入和输出
输入
使用标准库 std::io 来导入标准库的 io。
use std::io;
fn main() {
  let mut inp: String = String::new();
 	io::stdin.read_line(&mut inp).expect("Failed to read"); 
  println!("{inp}");
}- 使用 stdin引入标准输入流,
- 使用 read_line(&)来读取输入,
- 使用 expect()来处理异常情况。
输出
使用 println!() 来输出内容。
println!() 表示调用 macro,println() 表示调用函数
Rust 不支持直接输出变量的值,必须**格式化(format)**后输出。在引号 "" 中使花括号 {} 来输出变量。
fn main() {
    let var: u32 = 15;
  	println!("{}", var); // 15
  	// println!("var equals {var}")  Also ok!
  	// println!(var)  Error!
}结构体
Rust 的结构体类似于 C,使用关键字 struct 声明。
结构体中的每个元素称为“域”(field),域是可修改的(mutable),使用 . 来访问域的值。
struct User {
  active: bool,
  sign_in_count: u32,
  username: String,
  email: String
}
fn main() {
  let user = User {
    active: false,
    sign_in_count: 1,
    username: String::from("someusername"),
    email: String::from("someuseremail"),
  };
  
  user1.email = "anotheremail";
}可以使用 .. 来简化域成员的赋值:
struct User {
  active: bool,
  sign_in_count: u32,
  username: String,
  email: String
}
fn main() {
  let user = User {
    active: false,
    sign_in_count: 1,
    username: String::from("someusername"),
    email: String::from("someuseremail"),
  };
  
  let user_2 = User {
    active: true,
    ..user1
  }
}上面的代码表示,除了域 active 之外,user_2 的其他域值和 user 相等。
注:..user 后没有 ,。
枚举
例如下面的代码:
use std::cmp::Ordering;
use std:io;
fn main() {
		println!("This is a guessing game");
  	const SECRET_NUMBER: u32 = 12;
  	let mut guess: String = String::new();
  	println!("Enter your guess: ");
  	io::stdin.read_line(&mut guess).expect("Failed to read line");
  	let mut guess = match guess.parse().wrap() {
      Ok(num) => num,
      Err(_) => {
        println!("You should enter a number!");
      }
	  }
  	match guess.cmp::Ordering {
  		Less => println!("Too small!"),
      Greater => println!("Too big!"),
      Equal => println!("You are the winner!");
  }
}match 关键字用于开始匹配枚举。
这段代码中用到了两个枚举类型,分别是:
- 对 guess的类型判断会返回枚举类型Result,它有Ok和Err两个枚举值,分别表示成功和错误两种情况,
- 对 guess和SECRET_NUMBER两个值之间的比较会返回枚举类型Ordering,它有Less、Greater和Equal三种情况,分别表示小于,大于和等于。
待续










