Rust 基础概念
前言
Rust 是一门系统编程语言,它的目标是提供一种安全、并发、实用的编程语言。Rust 的设计借鉴了现有的一些编程语言,如 C++、Haskell、OCaml 等,但是它又有自己的特色,比如内存安全、线程安全等。本文旨在通过类比前端编程知识来解释 Rust 的语法
变量和数据类型
Rust 是一门静态类型语言,变量在使用前需声明,数据类型包括基本类型、复合类型和自定义类型。
基本类型
- 整数:有符号和无符号,如
i32
和 u64
。
- 浮点数:单精度
f32
和双精度 f64
。
- 布尔值:
bool
,取值为 true
或 false
。
- 字符:
char
,使用单引号表示。
整数
Rust 的整数类型包括有符号和无符号两种类型,分别有 8、16、32、64 和 arch(与机器架构相关)五种位数。
1let a: i32 = 42;
2let b: u64 = 0xff;
浮点数
Rust 的浮点数类型包括单精度(f32)和双精度(f64)两种。
1let a: f32 = 3.14;
2let b: f64 = 3.14159265358979323846;
布尔值
Rust 的布尔值类型为 bool
,有两个值:true
和 false
。
1let a: bool = true;
2let b: bool = false;
字符
Rust 的字符类型为 char
,使用单引号表示。
1let a: char = 'A';
2let b: char = '字';
复合类型
- 元组(tuple):包含多个类型的值的组合。
- 数组(array):固定长度的相同类型元素集合。
元组
元组是一个可以包含多个类型的值的组合,类似 TS 的元组。
1let a: (i32, f64, bool) = (42, 3.14, true);
访问元组中的值可以使用模式匹配或者使用点号加索引。
1let (x, y, z) = a;
2println!("x: {}, y: {}, z: {}", x, y, z); // 输出:x: 42, y: 3.14, z: true
3
4let first = a.0;
5let second = a.1;
6let third = a.2;
7println!("first: {}, second: {}, third: {}", first, second, third); // 输出:first: 42, second: 3.14, third: true
数组
数组是具有固定长度的相同类型元素的集合,类似 TS 的数组。
1let a: [i32; 5] = [1, 2, 3, 4, 5];
访问数组中的元素可以使用索引。
1let first = a[0];
2let second = a[1];
3println!("first: {}, second: {}", first, second); // 输出:first: 1, second: 2
自定义类型
- 结构体(struct):类似于 JavaScript 的对象。
- 枚举(enum):表示多种类型。
- Trait:定义共享行为。
结构体
Rust 的结构体(struct)类似于 JavaScript 的对象。结构体可以包含多个字段,每个字段需要指定类型。
1struct Point {
2 x: f64,
3 y: f64,
4}
5
6let p = Point { x: 1.0, y: 2.0 };
7println!("point: ({}, {})", p.x, p.y);
枚举
Rust 的枚举(enum)类似于 JavaScript 的类,但它可以表示多种类型。
1enum Message {
2 Quit,
3 Move { x: i32, y: i32 },
4 Write(String),
5 ChangeColor(i32, i32, i32),
6}
7
8let m = Message::Write(String::from("hello"));
Trait
Trait 是 Rust 中的一种特殊类型,用于定义共享行为。Trait 可以被其他类型实现,从而获得 Trait 中定义的方法。
1trait Printable {
2 fn print(&self);
3}
4
5struct Point {
6 x: i32,
7 y: i32,
8}
9
10impl Printable for Point {
11 fn print(&self) {
12 println!("({}, {})", self.x, self.y);
13 }
14}
15
16let p = Point { x: 1, y: 2 };
17p
控制流
Rust 支持常见的控制流结构,如 if
、else
、while
、for
等。
if 和 else
Rust 的 if 和 else 语法与 JavaScript 类似。
1let a = 42;
2
3if a < 0 {
4 println!("a is negative");
5} else if a > 0 {
6 println!("a is positive");
7} else {
8 println!("a is zero");
9}
while
Rust 的 while 语法与 JavaScript 类似。
1let mut a = 5;
2
3while a > 0 {
4 println!("a is {}", a);
5 a -= 1;
6}
for
Rust 的 for 语法可以用于遍历集合,如数组和范围。
1let a = [1, 2, 3, 4, 5];
2
3for element in a.iter() {
4 println!("the value is: {}", element);
5}
使用范围:
1for number in (1..4).rev() {
2 println!("{}!", number);
3}
4println!("LIFTOFF!!!");
函数
Rust 的函数定义使用 fn
关键字,参数和返回值需指定类型。
返回值类型使用 ->
指定。
1fn add(a: i32, b: i32) -> i32 {
2 a + b
3}
4
5let result = add(1, 2);
6println!("1 + 2 = {}", result);
这只是 Rust 语言的基本概念和语法,更多的功能和特性需要在实际项目中不断学习和探索。希望本文能为前端开发者提供一个 Rust 入门的参考。
错误处理
Rust 提供了一种强大的错误处理机制,可以帮助你编写健壮的代码。Rust 有两种错误类型:可恢复错误(recoverable errors)和不可恢复错误(unrecoverable errors)。Rust 使用 Result
枚举处理可恢复错误;使用 panic!
宏处理不可恢复错误。
可恢复错误
可恢复错误是那些可以通过返回一个 Result
类型来处理的错误。Result
类型是一个枚举类型,有两个变体:Ok
和 Err
。例如在这个示例中,尝试打开一个文件。如果文件打开成功,将得到一个 Ok 变体,其中包含一个 File 对象。
1use std::fs::File;
2
3fn main() {
4 let f = File::open("hello.txt");
5
6 match f {
7 Ok(file) => println!("File opened: {:?}", file),
8 Err(error) => println!("Failed to open file: {:?}", error),
9 }
10}
Result
Result 是一个枚举类型,用于表示可能出错的操作的结果。它有两个变体:Ok
和 Err
。
1fn divide(a: i32, b: i32) -> Result<i32, String> {
2 if b == 0 {
3 Err("division by zero".to_string())
4 } else {
5 Ok(a / b)
6 }
7}
8
9let a = divide(4, 2);
10let b = divide(4, 0);
可以使用模式匹配来处理 Result 类型的值。
1match a {
2 Ok(value) => println!("a: {}", value),
3 Err(error) => println!("a: error: {}", error),
4}
5
6match b {
7 Ok(value) => println!("b: {}", value),
8 Err(error) => println!("b: error: {}", error),
9}
不可恢复错误
不可恢复错误是那些无法通过返回一个 Result
类型来处理的错误。这类错误通常表示程序中的严重问题,需要立即终止程序的执行。在 Rust 中,可以使用 panic!
宏来触发不可恢复错误。例如在这个示例中,使用 panic!
宏来触发一个不可恢复错误。当程序执行到这一行时,它将立即终止,并显示一个错误消息。
1fn main() {
2 panic!("crash and burn");
3}
panic!
panic! 是一个宏,用于在遇到不可恢复错误时终止程序。例如
1fn main() {
2 panic!("This is an unrecoverable error.");
3}
模块系统
Rust 的模块系统用于组织和管理代码。模块系统包括以下几个部分:模块(module)、包(package)、库(library)和二进制(binary)。
模块
模块是 Rust 中的命名空间,用于将相关的代码组织在一起。可以使用 mod 关键字定义模块。
1mod math {
2 pub fn add(a: i32, b: i32) -> i32 {
3 a + b
4 }
5
6 pub fn subtract
7}
在一个模块中,可以使用 pub 关键字将函数、结构体、枚举等声明为公共的,这样它们就可以在其他模块中被访问。
1mod math {
2 pub fn add(a: i32, b: i32) -> i32 {
3 a + b
4 }
5
6 pub fn subtract(a: i32, b: i32) -> i32 {
7 a - b
8 }
9}
10
11fn main() {
12 let sum = math::add(1, 2);
13 let difference = math::subtract(3, 1);
14 println!("sum: {}, difference: {}", sum, difference);
15}
包和库
包(package)是一个包含一个或多个库(library)的项目。库是一组相关的模块。在 Rust 中,可以使用 Cargo 工具来创建和管理包和库。
要创建一个新的包,可以使用 cargo new 命令
这将创建一个名为 my_package 的新包,其中包含一个名为 my_package 的库。库的源代码位于 src/lib.rs 文件中。
要在包中添加一个新的库,可以在 src 目录下创建一个新的子目录,并在其中创建一个 lib.rs 文件。
二进制
二进制(binary)是一个可执行程序。在 Rust 中,可以使用 Cargo 工具来创建和管理二进制。
要创建一个新的二进制,可以使用 cargo new --bin 命令:
cargo new
这将创建一个名为 my_binary 的新二进制,其中包含一个名为 my_binary 的可执行程序。程序的源代码位于 src/main.rs 文件中。
要在包中添加一个新的二进制,可以在 src/bin 目录下创建一个新的 .rs 文件。
所有权与借用
所有权(ownership)
所有权是 Rust 中的一个核心概念,用于管理内存。在 Rust 中,每个值都有一个唯一的所有者,当所有者离开作用域时,值将被自动回收。这种机制可以避免内存泄漏和悬垂指针等问题。
1{
2 let s = String::from("hello");
3} // s 离开作用域,内存被回收
当一个值被赋值给另一个变量时,原变量将失去所有权,值的所有权将转移给新变量。
1let s1 = String::from("hello");
2let s2 = s1; // s1 失去所有权,s2 获得所有权
借用(borrowing)
借用是 Rust 中的另一个核心概念,用于在不转移所有权的情况下访问值。借用有两种形式:可变借用(mutable borrowing)和不可变借用(immutable borrowing)。
不可变借用
不可变借用是对值的只读访问。可以使用 & 符号创建不可变引用。
1fn print_length(s: &String) {
2 println!("length: {}", s.len());
3}
4
5let s = String::from("hello");
6print_length(&s); // 不可变借用 s
可变借用
可变借用是对值的可写访问。可以使用 &mut 符号创建可变引用。
1fn append_world(s: &mut String) {
2 s.push_str(" world");
3}
4
5let mut s = String::from("hello");
6append_world(&mut s); // 可变借用 s
7println!("{}", s); // 输出:hello world
注意:在同一作用域中
并发编程
Rust 提供了一些原生的并发编程特性,如线程、通道和互斥锁。这些特性可以编写高效且安全的并发代码。
线程
线程是操作系统中的并发执行单元。在 Rust 中,可以使用 std::thread
模块创建和管理线程。
1use std::thread;
2use std::time::Duration;
3
4fn main() {
5 let handle = thread::spawn(|| {
6 for i in 1..10 {
7 println!("thread: {}", i);
8 thread::sleep(Duration::from_millis(1));
9 }
10 });
11
12 for i in 1..5 {
13 println!("main: {}", i);
14 thread::sleep(Duration::from_millis(1));
15 }
16
17 handle.join().unwrap();
18}
通道
通道(channel)是一种用于在线程之间传递消息的同步原语。在 Rust 中,可以使用 std::sync::mpsc 模块创建和管理通道。
1use std::sync::mpsc;
2use std::thread;
3
4fn main() {
5 let (tx, rx) = mpsc::channel();
6
7 let handle = thread::spawn(move || {
8 let val = String::from("hi");
9 tx.send(val).unwrap();
10 });
11
12 let received = rx.recv().unwrap();
13 println!("Got: {}", received);
14
15 handle.join().unwrap();
16}
互斥锁
互斥锁(mutex)是一种用于在线程之间同步访问共享资源的同步原语。在 Rust 中,可以使用 std::sync::Mutex 类型创建和管理互斥锁。
1use std::sync::{Arc, Mutex};
2use std::thread;
3
4fn main() {
5 let counter = Arc::new(Mutex::new(0));
6 let mut handles = vec![];
7
8 for _ in 0..10 {
9 let counter = Arc::clone(&counter);
10 let handle = thread::spawn(move || {
11 let mut num = counter.lock().unwrap();
12 *num += 1;
13 });
14 handles.push(handle);
15 }
16
17 for handle in handles {
18 handle.join().unwrap();
19 }
20
21 println!("Result: {}", *counter.lock().unwrap());
22}
在这个示例中,使用 Arc(原子引用计数)和 Mutex(互斥锁)来同步访问一个共享的 counter 变量。创建了 10 个线程,每个线程都尝试对 counter 进行加 1 操作。通过使用互斥锁,确保了在任何时刻只有一个线程可以访问 counter。