Skip to content

rustup(版本管理工具,类似nodejs的nvm)

shell

# 更新Rust
rustup update 

# 卸载Rust和rustup
rustup self uninstall

# 检查是否安装成功
rustc -V
cargo -V

cargo(类似于npm)

常用命令

shell

# 运行项目(类似npm start)
cargo run

# 构建项目(类似npm run build)
cargo build

# 构建发布版本(优化后的二进制文件)
cargo build --release

# 运行测试(类似npm test)
cargo test  

# 检查代码(类似npm run lint)
cargo clippy

# 格式化代码(类似npm run format)
cargo fmt

{:?}和{}的区别(Debug和Display的区别)

std::fmt中的2个重要trait

Trait目的常用的格式化占位符典型实现方式
Display面向用户(end-user)的可读输出,适合在UI、日志、报表等场景中直接展示给人看的文字{}手动实现fmt(&self, f: &mut Formatter) -> fmt:Result或使用#[derive(Display)](通过外部宏)
Debug面向开发者的调试信息,倾向于提供解构体、枚举的完整内部状态,便于排错{:?} (单行) {:#?} (多行、缩进)大多数标准库类型已经实现;自定义类型可以#[derive(Debug)]自动生成;手动实现fmt::Debug亦可
  • 使用{:?}是一种惯例:在调试、快速打印变量时,程序员往往倾向于使用{:?},因为它不要求对象实现Display。几乎所有的标准库类型(包括自定义类型只要#[derive(Debug)])都有Debug实现;因此用{:?}可以一键打印,不必考虑是否实现Display

变量

变量分可变不可变,使用let声明,默认是不可变的,加上mut是声明可变的。

Rust

// 使用let来声明变量,a是不可变的
// 此处没有指定a的类型,自动推断位i32类型,有符号32位整数
let a = 10;
// 显式指定b的类型为i32(不可变)
let b: i32 = 20;
// c可变的,(值是30,i32是类型,也可以写成30_i32)
let mut c = 30i32;
// 对于未使用的变量,Rust会给警告,使用_开头,会让Rust忽略警告
let _x = 5;
// 变量解构
let (m, mut n): (bool, bool) = (true, false);

let (a, b, c, d);

(a, b) = (1, 2);
// _ 代表匹配一个值,不关心具体的值是什么,使用 _
[c, .., d, _] = [1, 2, 3, 4, 5];

不可变变量和const的对比

Preview

变量遮蔽

允许生成相同的变量名,后面声明的变量会覆盖前面声明的

变量遮蔽和mut可变变量的区别是:变量遮蔽是2个变量,只是恰好名字相同;mut声明的变量,是修改同一个内存地址上的值

Rust

let x = 5;
// x现在是6
let x = x + 1;
{
  let x = x * 2;
  // 这里输出12
  println!("The value of x in the inner space is: {}", x);
}
// 这里输出6
println!("The value of x is: {}", x);

类型

基本类型
  • 数值类型:
    • 有符号整数(i8,i16,i32,i64,isize)
    • 无符号整数(u8,u16,u32,u64,usize)
    • 浮点类型(f32,f64)
  • 字符串:字符串字面量和字符串切片&str
  • 布尔类型
  • 字符类型:单个Unicode字符,存储为4个字节
  • 单元类型:即(),唯一值也是()(是 “这里必须有一个类型,但实际值不重要” 的唯一实现,在运行时不占内存)

序列(Range)

只允许用于数字或字符类型,生成连续的数字或者字符,常用于循环

Rust

for i in 1..=5 {
  println!("{}", i);
}

for i in 'a'..='z' {
  println!("{}", i)
}
复合类型

语句和表达式

语句执行操作,不返回
表达式求值后返回一个值(表达式没有分号。调用函数是表达式,调用宏是表达式,花括号包裹最终返回一个值的语句块也是表达式。表达式如果不返回任何值,隐式返回())

函数

Rust

fn add(i: i32, j: i32) -> i32 {
  i + j
}
  • 函数名和变量名使用蛇形命名法,比如fn add_two() {}
  • 函数位置随便放,有定义就行
  • 每个函数参数都要标注类型
  • 函数分返回值就是函数最后一条表达式的返回值,也可以用return提前返回
  • 如果函数没有返回值,返回()
  • 返回!的函数,表示发散函数,意思是函数用不会返回,用做导致程序崩溃的函数

所有权和借用

*&符号作用对比

位置代码片段语义结果类型示例
声明int *p
char *c
p是指向int的指针
c是指向char的指针
int *char *
(指针类型)
int *p = NULL;
表达式*p解引用:取指针p所指向的对象的值int(若pint *)int v = *p;
表达式&a取地址:把对象a的内存地址形成指针int *aintint *q = &a;
函数返回类型int* foo()foo返回一个指向int的指针int *int *ptr = foo()