Rust 入门

仅仅介绍 Rust 的安装、Cargo、变量、数据类型、函数和控制流

环境:Ubuntu 22.04 rustc 1.68.2

安装

1
2
3
4
5
6
7
8
# 安装
$ curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh

# 更新
$ rustup update

# 卸载
$ rustup self uninstall

安装时会下载 rustup 工具,并安装最新版 Rust 一系列工具(编译器 rustc 等)

rustup 是一个管理 Rust 版本和相关工具的命令行工具

Hello world!

程序员传统捏~

hello.rs
1
2
3
4
5
// 使用 rustc hello.rs 编译

fn main() {
println!("Hello world!");
}

main 函数:不用多说,程序入口

println!:调用了一个宏(如果是调用函数,后面没有!

"Hello world!":将字符串传递给 println

分号 ; 结尾:大部分语句都以问号结尾

Cargo

Cargo 是 Rust 的构建系统和包管理器。大多数 Rustacean 们使用 Cargo 来管理他们的 Rust 项目,因为它可以为你处理很多任务,比如构建代码、下载依赖库并编译这些库。

下面的命令会创建一个 hello_cargo 的项目

1
$ cargo new hello_cargo

里面有一个 Cargo.toml 文件,这是 Cargo 的配置文件

Cargo.toml
1
2
3
4
5
6
7
8
[package]
name = "hello_cargo"
version = "0.1.0"
edition = "2021"

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

[dependencies]

[package]:表明下面为一个包的配置

[dependencies]:罗列项目使用的依赖,依赖的代码包也被称为 crate

构建并运行项目

下面的命令用于构建项目,并生成 target 文件夹和 Cargo.lock 文件

1
$ cargo build

target/debug/hello_carge 为编译出来的可执行文件,我们可以直接运行这个文件,也可以使用下面的命令来编译并运行项目,更加方便

1
2
3
4
5
$ cargo run
  Compiling hello_cargo v0.1.0 (/home/humoooor/Code/RustPractice/hello_cargo)
   Finished dev [unoptimized + debuginfo] target(s) in 0.46s
    Running `target/debug/hello_cargo`
Hello, world!

下面的命令可以检查代码确保可以编译

1
2
3
$ cargo check
   Checking hello_cargo v0.1.0 (/home/humoooor/Code/RustPractice/hello_cargo)
   Finished dev [unoptimized + debuginfo] target(s) in 0.20s

发布项目

在构建时使用 --release 参数,来优化编译项目,并在 target/release 目录下生成可执行文件,启用优化编译时间更长,但是运行速度更快,没有调试信息

1
2
3
$ cargo build --release
  Compiling hello_cargo v0.1.0 (/home/humoooor/Code/RustPractice/hello_cargo)
   Finished release [optimized] target(s) in 0.19s

变量和可变性

变量定义

1
2
3
4
5
let [mut] {var_name}: {var_type} = {value};

let x = "-1";
// 将 x 从 "-1" 转换成 -1
let x: i32 = x.parse().except("");

value_type 和 value 必须出现一个,在某些情况(如类型转换)下,两者都要出现

每次只可以定义一个变量

可变性 mutable

在 Rust 中,变量默认是不可改变的(immutable),在声明变量时在变量名前加上 mut 使其具有可变性

1
2
3
4
5
6
7
8
9
fn main() {dd
// x 默认不可变,会出现编译错误
let x = 5;
// 添加 mut 后,允许 x 的值改变
// let mut x = 5;
println!("value of x: {x}");
x = 6;
println!("value of x: {x}");
}

常量 const

1
2
3
const {const_name}: {const_type} = {const_value}

const PI: f32 = 3.14;

必须注明常量类型

隐藏

一个变量名可以重复声明,便于在类型转换等情况时复用变量名,实际上是创建了一个新变量,之前的变量会被隐藏,直到新变量的作用域结束

1
2
3
4
5
6
7
8
9
10
11
12
13
fn main() {
let x = "-5";
{
let mut x: i32 = x.parse().expect("Not a number");
x = x + 5;
println!("x = {x}");
}
println!("x = {x}");
}

// output:
// x = 0
// x = -5

基本数据类型

Rust 有标量和复合两类数据类型

标量类型

整型 integer

Len signed unsigned
8-bit i8 i8
32-bit i32 i32
64-bit i64 i64
128-bit i128 i128
arch isize isize
Rust 默认类型为 i32

arch 依赖计算机架构,64 位架构 isize 就是 64 位

数字可使用 _ 作为分隔符,方便读数,如 1000 表示为 1_000

也可以使用类型后缀来指定数字类型,如 57u8 为无符号 8-bit 整型

字面值 例子
Hex 0xff
Decimal 0o77
Octal 99
Binary 0b11
Byte b’a’

浮点型 float

Rust 浮点数类型有 f32f64,默认为 f64 类型,精度更高

布尔型 bool

truefalse 两个值

字符型 char

1
2
let c = 'z';
let heart_eyed_cat: char = '😻';

这里的 char 大小是四个字节,Unicode 编码,可以表示比 ASCII 更多的内容,如中日韩文、emoji 等

字符型的值必须使用单引号表示,双引号为字符串

复合类型

元组 tuple

元组可以将多个类型的值组合进一个复合类型,长度不可变,类似 C 语言的结构体

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

类型可省略,省略后为默认类型

从元组上取值

1
2
3
4
5
6
7
8
let tup: (i32, f64, u8) = (500, 6.4, 1);

// 解构,destructuring
let (x, y, z) = tup;
// 索引
let x = tup.0;
let y = tup.1;
let z = tup.2;

不带任何值的元组,称为单元元组

数组 array

数组的元素类型必须相同,长度固定

1
2
3
4
5
6
7
8
9
10
// 数组声明

let a = [1, 2, 3, 4];
// 长度为 4,元素类型为 i32 的数组
let a: [i32; 4] = [1, 2, 3, 4];
// 长度为 5,元素全为 3 的数组
let a = [3, 5];

// 数组元素访问
let first = a[0];

越界访问会直接导致 panic,这是 Rust 的一个安全机制

函数

使用 fn 关键字声明函数,函数名和变量名使用 snake case 规范风格,字母全部小写

Rust 不像 C 语言需要在 调用函数 的前面声明 被调用函数

1
2
3
4
5
6
7
8
9
10
11
12
13
fn {func_name} ({var_name1}: {var_type1}, ..) -> {ret_type}{
...
}

fn main() {
let x = 1;
let y = 2;
println!("{x} + {y} = {}", my_func(x, y));
}

fn my_func(x: i32, y: i32) -> i32 {
return x + y;
}

必须指定参数类型

表达式和语句

Rust 是基于表达式的语言

表达式:计算并产生一个值。大部分 Rust 代码由表达式组成,数学运算、函数调用、宏调用、大括号创建的块作用于都是一个表达式

语句:执行一些操作但不返回值的指令。当表达式结尾加上分号时,它就变成了语句。函数定义也是一个语句

1
2
3
4
5
6
7
8
9
let y = {
let x = 3;
x + 1
};

println!("{y}")

// output:
// 4

这里的代码块 {let x = 3; x + 1} 由于结尾没有分号,是一个表达式,返回值是 4

在有返回值的函数中可以主动使用 return 返回值,也可以隐式地返回函数中最后的表达式

1
2
3
4
5
6
7
fn my_func(x: i32, y: i32) -> i32 {
return x + y;
}

fn my_func(x: i32, y: i32) -> i32 {
x + y
}

在返回表达式时,结尾不可加分号,否则它就不是表达式了

控制流

Rust 中控制流的表达式必须返回 bool 类型

if-else

1
2
3
4
5
6
7
8
9
10
11
let number = 6;

if number % 4 == 0 {
println!("number is divisible by 4");
} else if number % 3 == 0 {
println!("number is divisible by 3");
} else if number % 2 == 0 {
println!("number is divisible by 2");
} else {
println!("number is not divisible by 4, 3, or 2");
}

let 语句中使用 if-else

1
2
3
4
5
6
7
let condition = true;
let number = if condition { 5 } else { 6 };

println!("The value of number is: {number}");

// output:
// 5

loop、while、for

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 无限循环
loop {
println!("again!");
}

let a = [1, 2, 3, 4, 5];
let mut i = 0;

// 两种遍历数组的方式
while i < a.len() {
println!("a[{i}] = {}", a[i]);
i += 1;
}

// 0..a.len() 相当于 Python 的 range(0, len(a))
for i in 0..a.len() {
println!("a[{i}] = {}", a[i]);
}

break、continue

简单的 breakcontinue 可以跳出一层循环

breakcontinue 后加上表达式,可以返回值

想要跳出嵌套循环,在指定循环前标记一个循环标签,与 breakcontinue 一起使用,可以作用于标记的循环,循环标签前需要加单引号 '

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
loop {
println!("again!");
break;
}

// res = 20
let res = loop {
counter += 1;

if counter == 10 {
break counter * 2;
}
}

// 嵌套循环
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}");
作者

Humoooor

发布于

2023-04-04

更新于

2023-10-04

许可协议

评论