0
点赞
收藏
分享

微信扫一扫

Rust 笔记

mjjackey 2023-05-10 阅读 64

The Rust Programming Language

Rust 编程语言笔记。

安装

使用 rustup 来安装 Rust

Rust 源文件以 .rs 作为扩展名。

几个常用的命令:

  • 编译:rustc .rs-file
  • 运行:./compiled-file
  • 检查 Rust 编译器版本:rustc --version
  • 检查 rustup 版本:rustup --version
  • 更新 Rust 编译器:rustup update
  • 卸载 Rustrustuprustup 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; // Error

shadowing

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 的区别

  1. shadow 使得可以改变同名变量的类型,但是 mut 会在编译时出错

fn main() {
    let x: &str = "     ";
	  let x: usize = x.len(); // 5
  
    let mut x: &str = "     ";
    x = x.len()             // Error
}

  1. 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";

不可修改的变量和常量的区别在于:

  1. 常量只能是不可修改的,不能通过 mut 关键字使得常亮可以修改
  2. 常量通常只赋值给常量声明的表达式,而不是运行时的计算表达式
  3. 常量可以在任何作用域(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

布尔型

布尔型有两个值:

  1. true
  2. 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
  • 在使用集合类数据类型的索引时,使用 usizeisize

浮点型

浮点型有两类,而且都是有符号的:

  • 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!");
  }
}

注:和 JavaScriptRuby 等语言不同,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");
}

continueloop 循环无效。

可以用 ``nameloop` 循环命名(标识符)以跳出多层循环,例如:

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!() 表示调用 macroprintln() 表示调用函数

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 关键字用于开始匹配枚举。

这段代码中用到了两个枚举类型,分别是:

  1. guess 的类型判断会返回枚举类型 Result,它有 OkErr 两个枚举值,分别表示成功和错误两种情况,
  2. guessSECRET_NUMBER 两个值之间的比较会返回枚举类型 Ordering,它有 LessGreaterEqual 三种情况,分别表示小于,大于和等于。

待续

举报

相关推荐

0 条评论