Rust语言教程:从基础到高级编程全面指南
Rust 语言教程:从基础到高级编程全面指南
Rust 是一门系统级编程语言,以其内存安全、并发性能和零成本抽象而著称。它被设计用来构建高性能、可靠的软件,例如操作系统、游戏引擎、WebAssembly 模块以及各种需要高性能和安全性的应用。本教程将带领你从 Rust 的基础知识逐步深入到高级编程概念,让你全面掌握这门强大的语言。
第一部分:Rust 基础
1.1 安装与配置
首先,你需要安装 Rust 编译器和包管理器 Cargo。访问 Rust 官方网站 https://www.rust-lang.org/ ,按照说明下载并安装适合你操作系统的 Rust 工具链。
安装完成后,在终端或命令行中输入以下命令来验证安装是否成功:
bash
rustc --version
cargo --version
如果显示出版本号,则表示安装成功。
1.2 Hello, World!
按照惯例,我们从一个简单的 "Hello, World!" 程序开始:
rust
fn main() {
println!("Hello, World!");
}
将这段代码保存为 main.rs
文件。然后,在终端中进入该文件所在的目录,使用 Cargo 构建并运行程序:
bash
cargo new hello_world //创建一个新的cargo 项目
cd hello_world //进入项目目录
cargo run //编译运行
你会在终端看到输出 "Hello, World!"。
代码解释:
fn main()
:定义了程序的入口点,main
函数是每个 Rust 可执行程序必须有的。println!("Hello, World!");
:这是一个宏(macro),用于将文本打印到控制台。!
表明它是一个宏调用。
1.3 变量与数据类型
Rust 是静态类型语言,这意味着在编译时必须知道所有变量的类型。
1.3.1 变量声明
使用 let
关键字声明变量。Rust 具有类型推断能力,通常不需要显式指定类型:
rust
let x = 5; // Rust 推断 x 为 i32 类型
let y: f64 = 3.14; // 显式指定 y 为 f64 类型
1.3.2 可变性
默认情况下,Rust 中的变量是不可变的(immutable)。要声明可变变量,使用 mut
关键字:
rust
let mut z = 10;
z = 20; // 可以修改 z 的值
1.3.3 基础数据类型
Rust 有以下几种基础数据类型:
- 整数类型:
i8
,i16
,i32
,i64
,i128
,isize
(有符号整数)和u8
,u16
,u32
,u64
,u128
,usize
(无符号整数)。isize
和usize
的大小取决于你的计算机架构(32 位或 64 位)。 - 浮点数类型:
f32
(单精度)和f64
(双精度)。 - 布尔类型:
bool
,值为true
或false
。 - 字符类型:
char
,表示一个 Unicode 标量值(4 个字节)。 - 字符串类型:
&str
字符串切片,String
可变字符串。
rust
let a: i32 = -10;
let b: u64 = 100;
let c: f32 = 2.5;
let d: bool = true;
let e: char = '🚀';
let str_slice: &str = "hello"; //字符串切片
let mut my_string: String = String::from("Hello"); //可变字符串
my_string.push_str(", world!");
1.4 运算符
Rust 支持常见的算术、比较、逻辑和位运算符:
```rust
let sum = 5 + 10;
let difference = 10 - 5;
let product = 4 * 30;
let quotient = 56.7 / 32.2;
let remainder = 43 % 5;
let and = true && false;
let or = true || false;
let not = !true;
let greater = 10 > 5;
let less_equal = 5 <= 5;
```
1.5 控制流
1.5.1 if
表达式
```rust
let number = 7;
if number < 5 {
println!("condition was true");
} else if number == 5 {
println!("number is 5");
} else {
println!("condition was false");
}
```
if
表达式可以有返回值:
rust
let condition = true;
let number = if condition { 5 } else { 6 };
1.5.2 loop
循环
loop
关键字创建一个无限循环,通常与 break
结合使用:
```rust
let mut counter = 0;
loop {
counter += 1;
if counter == 10 {
break; // 退出循环
}
}
```
loop
循环也可以有返回值:
```rust
let result = loop {
counter += 1;
if counter == 10 {
break counter * 2; //返回 counter*2
}
};
```
1.5.3 while
循环
```rust
let mut number = 3;
while number != 0 {
println!("{}", number);
number -= 1;
}
```
1.5.4 for
循环
for
循环用于遍历一个迭代器(例如范围、数组、向量):
```rust
for number in (1..4).rev() { //迭代1到4,不包含4
println!("{}", number);
}
let a = [10, 20, 30, 40, 50];
for element in a.iter() {
println!("the value is: {}", element);
}
```
1.6 函数
使用 fn
关键字定义函数:
```rust
fn greet(name: &str) {
println!("Hello, {}!", name);
}
fn add(x: i32, y: i32) -> i32 {
x + y // 表达式作为返回值,不需要 return 关键字
}
fn main(){
greet("Alice");
let sum = add(5,3);
println!("{}",sum);
}
```
fn greet(name: &str)
:定义了一个名为greet
的函数,它接受一个字符串切片(&str
)类型的参数name
。fn add(x: i32, y: i32) -> i32
:定义了一个名为add
的函数,它接受两个i32
类型的参数x
和y
,并返回一个i32
类型的值。- 在 Rust 中,函数的最后一个表达式的值会被隐式返回,因此不需要
return
关键字(当然,你也可以使用return
来提前返回)。
1.7 注释
Rust 支持两种注释方式:
- 单行注释:
//
- 多行注释:
/* ... */
- 文档注释:
///
(用于函数,模块等的文档)
```rust
// 这是单行注释
/
这是
多行注释
/
/// 计算两个数的和。文档注释
///
/// # Examples
///
/// /// let result = add(2, 3);
/// assert_eq!(result, 5);
///
fn add(a: i32, b: i32) -> i32 {
a + b
}
```
第二部分:Rust 核心概念
2.1 所有权(Ownership)
所有权是 Rust 最核心、最独特的概念,它保证了 Rust 程序的内存安全,避免了悬垂指针、数据竞争等问题。理解所有权是掌握 Rust 的关键。
所有权规则:
- 每个值都有一个被称为其所有者的变量。
- 在同一时间,只能有一个所有者。
- 当所有者超出作用域时,该值将被丢弃(释放内存)。
rust
{
let s = String::from("hello"); // s 进入作用域
// ... 使用 s ...
} // s 超出作用域,s 拥有的内存被释放(调用 drop 函数)
2.1.1 移动(Move)
当将一个变量赋值给另一个变量时,会发生所有权的转移(移动):
```rust
let s1 = String::from("hello");
let s2 = s1; // s1 的所有权移动到 s2
// println!("{}", s1); // 错误!s1 不再有效
println!("{}", s2);
```
这里,s1
的值被移动到 s2
,s1
不再有效。这是因为 String
类型的数据存储在堆上,如果两个变量都指向同一块内存,会导致 double free 的问题。
2.1.2 克隆(Clone)
如果你需要复制堆上的数据,可以使用 clone
方法:
```rust
let s1 = String::from("hello");
let s2 = s1.clone(); // 深拷贝 s1 的数据
println!("s1 = {}, s2 = {}", s1, s2); // s1 和 s2 都有效
```
2.1.3 拷贝(Copy)
对于存储在栈上的数据类型(例如整数、浮点数、布尔值、字符、元组),赋值操作会进行拷贝:
```rust
let x = 5;
let y = x; // 拷贝 x 的值
println!("x = {}, y = {}", x, y); // x 和 y 都有效
```
2.2 借用(Borrowing)
借用允许你引用一个值而不获取其所有权。借用使用 &
符号表示。
2.2.1 不可变借用
```rust
fn calculate_length(s: &String) -> usize { // s 是对 String 的引用
s.len()
} // s 在这里超出作用域,但它并不拥有 String,所以 String 不会被丢弃
fn main(){
let s1 = String::from("hello");
let len = calculate_length(&s1); // 传递 s1 的引用
println!("The length of '{}' is {}.", s1, len); //s1仍然有效
}
```
2.2.2 可变借用
使用 &mut
创建可变借用,允许你修改引用的值:
```rust
fn change(some_string: &mut String) {
some_string.push_str(", world");
}
fn main() {
let mut s = String::from("hello");
change(&mut s);
println!("{}",s);
}
```
借用规则:
- 在同一时间,你可以有多个不可变借用,或者只有一个可变借用。
- 引用必须始终有效(不能引用已经释放的内存)。
这些规则由 Rust 的借用检查器(borrow checker)在编译时强制执行,确保了内存安全。
2.3 生命周期(Lifetimes)
生命周期是 Rust 用来确保引用始终有效的机制。它们是泛型的一种,用于描述引用的有效范围。
基本语法:
rust
&'a i32 // 具有生命周期 'a 的 i32 引用
&'a mut i32 // 具有生命周期 'a 的可变 i32 引用
生命周期注解通常用于函数签名中,以指定输入引用和输出引用之间的关系:
rust
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
这个函数的签名表示:x
和 y
这两个字符串切片具有相同的生命周期 'a
,并且返回的字符串切片也具有相同的生命周期 'a
。这意味着返回的引用不会比 x
和 y
中的任何一个活得更久。
生命周期省略规则
在很多情况下,你可以省略生命周期注解,Rust 编译器会根据一些规则自动推断:
- 每个引用参数都有一个自己的生命周期参数。
- 如果只有一个输入生命周期参数,那么该生命周期会被赋给所有输出生命周期参数。
- 如果有多个输入生命周期参数,但其中一个是
&self
或&mut self
,那么self
的生命周期会被赋给所有输出生命周期参数。
第三部分:Rust 高级特性
3.1 结构体(Structs)
结构体用于创建自定义数据类型:
```rust
struct Point {
x: i32,
y: i32,
}
fn main() {
let origin = Point { x: 0, y: 0 };
println!("The origin is at ({}, {})", origin.x, origin.y);
let mut p = Point{x:1,y:2}; //可变结构体
p.x = 10;
}
```
3.2 枚举(Enums)
枚举用于定义一个类型,该类型可以是几个不同值中的一个:
```rust
enum Message {
Quit,
Move { x: i32, y: i32 }, // 包含命名字段
Write(String), // 包含一个 String
ChangeColor(i32, i32, i32), // 包含三个 i32
}
fn main(){
let m1 = Message::Quit;
let m2 = Message::Move{x:10,y:20};
}
```
3.2.1 Option
枚举
Option
是一个标准库中定义的枚举,用于表示一个值可能存在或不存在的情况:
rust
enum Option<T> {
Some(T),
None,
}
它用于处理可能失败的操作,避免了空指针异常。
```rust
fn divide(numerator: f64, denominator: f64) -> Option
if denominator == 0.0 {
None
} else {
Some(numerator / denominator)
}
}
fn main(){
let result = divide(10.0,2.0);
match result {
Some(value) => println!("Result: {}", value),
None => println!("Cannot divide by zero."),
}
}
```
3.3 模式匹配(Pattern Matching)
match
表达式允许你将一个值与一系列模式进行比较,并执行匹配的模式对应的代码:
```rust
let number = 1;
match number {
1 => println!("One"),
2 => println!("Two"),
3 => println!("Three"),
_ => println!("Something else"), // _ 匹配所有其他情况
}
```
match
还可以用于解构枚举、结构体等:
```rust
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
fn main(){
let msg = Message::Move { x: 10, y: 20 };
match msg {
Message::Quit => println!("Quit"),
Message::Move { x, y } => println!("Move to x: {}, y: {}", x, y),
Message::Write(text) => println!("Text message: {}", text),
Message::ChangeColor(r, g, b) => println!("Change color to r: {}, g: {}, b: {}", r, g, b),
}
}
```
3.4 泛型(Generics)
泛型允许你编写可以处理多种类型的代码,而无需为每种类型编写单独的代码:
```rust
fn largest
let mut largest = list[0];
for &item in list.iter() {
if item > largest {
largest = item;
}
}
largest
}
fn main() {
let number_list = vec![34, 50, 25, 100, 65];
let result = largest(&number_list);
println!("The largest number is {}", result);
let char_list = vec!['y', 'm', 'a', 'q'];
let result = largest(&char_list);
println!("The largest char is {}", result);
}
```
fn largest<T: PartialOrd + Copy>(list: &[T]) -> T
:定义了一个泛型函数largest
,它接受一个类型为T
的切片,并返回一个类型为T
的值。T: PartialOrd + Copy
:这是一个 trait bound(特征约束),它指定了类型T
必须实现PartialOrd
(用于比较)和Copy
(用于拷贝)这两个 trait。
3.5 特征(Traits)
特征定义了共享行为。它们类似于其他语言中的接口(interfaces):
```rust
trait Summary {
fn summarize(&self) -> String;
}
struct NewsArticle {
headline: String,
location: String,
author: String,
content: String,
}
impl Summary for NewsArticle {
fn summarize(&self) -> String {
format!("{}, by {} ({})", self.headline, self.author, self.location)
}
}
fn main() {
let article = NewsArticle {
headline: String::from("Penguins win the Stanley Cup Championship!"),
location: String::from("Pittsburgh, PA, USA"),
author: String::from("Iceburgh"),
content: String::from(
"The Pittsburgh Penguins once again are the best \
hockey team in the NHL.",
),
};
println!("New article available! {}", article.summarize());
}
```
trait Summary
:定义了一个名为Summary
的 trait,它有一个summarize
方法。impl Summary for NewsArticle
:为NewsArticle
结构体实现了Summary
trait。
3.6 错误处理
Rust 使用 Result
枚举来处理可能失败的操作:
rust
enum Result<T, E> {
Ok(T),
Err(E),
}
Ok(T)
:表示操作成功,并包含结果值T
。Err(E)
:表示操作失败,并包含错误值E
。
```rust
use std::fs::File;
use std::io::Read;
fn read_username_from_file() -> Result
let mut f = File::open("username.txt")?;
let mut s = String::new();
f.read_to_string(&mut s)?;
Ok(s)
}
fn main(){
let result = read_username_from_file();
match result {
Ok(username) => println!("Username: {}", username),
Err(error) => println!("Error: {}", error),
}
}
```
?
运算符用于简化错误传播。如果 File::open
或 read_to_string
返回 Err
,?
会将错误返回给调用者。
3.7 并发(Concurrency)
Rust 的所有权和类型系统使其能够安全、高效地处理并发。
3.7.1 线程(Threads)
```rust
use std::thread;
use std::time::Duration;
fn main() {
let handle = thread::spawn(|| {
for i in 1..10 {
println!("hi number {} from the spawned thread!", i);
thread::sleep(Duration::from_millis(1));
}
});
for i in 1..5 {
println!("hi number {} from the main thread!", i);
thread::sleep(Duration::from_millis(1));
}
handle.join().unwrap(); // 等待 spawned 线程结束
}
```
3.7.2 消息传递(Message Passing)
```rust
use std::sync::mpsc;
use std::thread;
fn main() {
let (tx, rx) = mpsc::channel(); // 创建一个通道
thread::spawn(move || {
let val = String::from("hi");
tx.send(val).unwrap(); // 发送消息
});
let received = rx.recv().unwrap(); // 接收消息
println!("Got: {}", received);
}
```
mpsc::channel()
:创建一个通道,返回一个发送者(tx
)和一个接收者(rx
)。tx.send(val)
:发送消息到通道。rx.recv()
:从通道接收消息。
3.7.3 共享状态(Shared State)
```rust
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let counter = Arc::new(Mutex::new(0)); // 使用 Arc 和 Mutex 实现共享可变状态
let mut handles = vec![];
for _ in 0..10 {
let counter = Arc::clone(&counter);
let handle = thread::spawn(move || {
let mut num = counter.lock().unwrap(); // 获取锁
*num += 1; // 修改共享数据
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
println!("Result: {}", *counter.lock().unwrap());
}
```
Arc
:原子引用计数指针,允许多个线程拥有相同的数据。Mutex
:互斥锁,用于保护共享数据,确保在同一时间只有一个线程可以访问数据。
总结
本教程涵盖了 Rust 语言的广泛主题,从基础语法到高级概念。通过学习本教程,你应该已经掌握了 Rust 的核心概念,包括所有权、借用、生命周期、结构体、枚举、模式匹配、泛型、特征、错误处理和并发。
要成为一名熟练的 Rust 开发者,还需要不断练习和探索。阅读 Rust 官方文档(https://doc.rust-lang.org/book/),参与 Rust 社区,并尝试编写自己的项目,这些都是提高 Rust 技能的好方法。祝你在 Rust 编程之旅中取得成功!