Rust,是一门专注于安全、并发和性能的系统编程语言,由Mozilla主导开发,2015年5月发布了1.0版本。
到2018年,Rust已经连续三年被Stack Overflow开发者调查评为"最受喜爱的编程语言"。越来越多的公司和开发者开始关注和使用Rust,从系统编程到Web开发,从游戏引擎到区块链,Rust的应用场景越来越广。
我自己接触Rust,是在2017年。那时候,Rust已经发布了1.0,生态也逐渐完善,我抱着好奇的心态学了一下,没想到一学就入迷了。Rust的设计理念,特别是它的所有权系统,让我对编程有了新的认识。
今天,我想写一篇Rust入门指南,从Rust的特点、安装配置、基本语法、核心概念,到学习资源,带你从零开始了解这门语言。如果你对Rust感兴趣,但是不知道从哪里开始,这篇文章应该能帮到你。
一、Rust是什么,为什么要学Rust
在开始学习之前,先搞清楚一个问题:Rust是什么?为什么要学Rust?
Rust是什么?
Rust是一门系统编程语言,它的设计目标是:安全、并发、性能。
- 安全:Rust在编译期就保证了内存安全和线程安全,不需要垃圾回收器,也不会出现空指针、野指针、数据竞争等问题。
- 并发:Rust的类型系统和所有权模型,天然支持安全的并发编程,让你写出高效且安全的并发代码。
- 性能:Rust的性能和C/C++相当,没有运行时开销,没有垃圾回收停顿,可以用来写操作系统、浏览器引擎、游戏引擎等对性能要求极高的软件。
简单说,Rust想要做到的是:像C/C++一样高性能,但是比C/C++更安全;像Java/Python一样安全,但是比它们更高性能。
为什么要学Rust?
- 内存安全,没有空指针和野指针:Rust在编译期就检查内存安全,很多C/C++中常见的内存错误(空指针解引用、野指针、缓冲区溢出、使用已释放内存等),在Rust中编译都通不过。这大大减少了运行时的bug和安全漏洞。
- 没有垃圾回收,性能高:Rust不需要垃圾回收器,通过所有权系统在编译期管理内存,运行时没有GC停顿,性能和C/C++相当。
- 并发安全:Rust的类型系统,能在编译期防止数据竞争,让并发编程更安全、更简单。
- 现代的语言特性:Rust有模式匹配、泛型、trait、迭代器、闭包等现代语言特性,表达能力强,代码优雅。
- 优秀的工具链:Rust有统一的包管理工具Cargo,有内置的测试框架、文档生成、格式化工具,开发体验很好。
- 社区活跃,生态 growing:Rust的社区非常活跃,生态正在快速发展,从系统编程到Web开发,从嵌入式到区块链,都有Rust的身影。
当然,Rust也不是完美的。它的学习曲线比较陡峭,特别是所有权、借用、生命周期这些概念,初学者需要花一些时间才能理解。而且,Rust的编译速度比较慢,有时候改一行代码要等很久才能编译完。但是,这些缺点,相对于它带来的好处来说,是值得的。
二、安装和配置
学习Rust的第一步,是安装Rust工具链。
Rust的官方推荐安装方式,是使用rustup,一个Rust版本管理工具。
在Linux/macOS上安装:
curl https://sh.rustup.rs -sSf | sh运行这个命令之后,会出现一个安装提示,选择默认安装就行。安装完成后,需要把Rust的bin目录加到PATH环境变量中。安装脚本会自动帮你配置,但是你可能需要重启终端,或者手动执行:
source $HOME/.cargo/env在Windows上安装:
Windows上,需要先下载rustup-init.exe,然后运行它。你可以从Rust官网下载:https://www.rust-lang.org/tools/install
运行之后,按照提示安装就行。注意,Windows上安装Rust,可能需要先安装Visual C++ Build Tools,因为Rust需要链接器。
验证安装:
安装完成后,打开终端,运行:
rustc --version
cargo --version如果能看到版本号,说明安装成功了。
更新Rust:
Rust的更新很频繁,大概每6周发布一个新版本。更新的时候,只需要运行:
rustup update就可以更新到最新的稳定版本。
三、第一个Rust程序
安装好之后,我们来写第一个Rust程序:Hello World。
创建一个文件,叫main.rs,内容如下:
fn main() {
println!("Hello, World!");
}然后,用rustc编译:
rustc main.rs编译成功后,会生成一个可执行文件。运行它:
# Linux/macOS
./main
# Windows
main.exe你会看到输出:Hello, World!
这就是你的第一个Rust程序。
不过,在实际开发中,我们一般不会直接用rustc编译,而是用Cargo,Rust的包管理和构建工具。
用Cargo创建一个新项目:
cargo new hello_world
cd hello_worldCargo会自动创建项目结构:
hello_world/
├── Cargo.toml # 项目配置文件
└── src/
└── main.rs # 源代码Cargo.toml是项目的配置文件,内容大概是这样的:
[package]
name = "hello_world"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
[dependencies]src/main.rs的内容,就是我们刚才写的Hello World程序。
用Cargo构建和运行:
cargo build # 构建项目
cargo run # 构建并运行Cargo会自动处理依赖、编译、链接,非常方便。
四、Rust的基本语法
了解了怎么写和运行Rust程序之后,我们来看看Rust的基本语法。
1. 变量和可变性
在Rust中,变量默认是不可变的(immutable)。也就是说,你声明了一个变量之后,不能再修改它的值。如果你想修改,需要用mut关键字声明为可变变量。
fn main() {
let x = 5; // 不可变变量
// x = 6; // 这行会编译错误!
let mut y = 5; // 可变变量
y = 6; // 这行是可以的
println!("y = {}", y);
}Rust的这个设计,是为了安全。默认不可变,能防止意外修改变量,减少bug。如果你需要修改变量,显式声明mut,让代码的意图更清晰。
2. 数据类型
Rust是静态类型语言,每个变量都有确定的类型。但是,Rust有类型推断,很多时候你不需要显式声明类型,编译器会帮你推断。
Rust的基本数据类型包括:
- 整数:i8, i16, i32, i64, u8, u16, u32, u64, isize, usize
- 浮点数:f32, f64
- 布尔值:bool
- 字符:char
- 元组:(i32, f64, bool)
- 数组:[i32; 5]
fn main() {
let a: i32 = 42;
let b: f64 = 3.14;
let c: bool = true;
let d: char = '中'; // Rust的char是Unicode的,4字节
let tuple: (i32, f64, bool) = (42, 3.14, true);
let array: [i32; 5] = [1, 2, 3, 4, 5];
}3. 函数
Rust的函数,用fn关键字声明。参数必须声明类型,返回值用->声明。
fn add(a: i32, b: i32) -> i32 {
a + b // 注意,这里没有分号,这是表达式,是返回值
}
fn main() {
let result = add(3, 5);
println!("3 + 5 = {}", result);
}Rust中,语句和表达式是有区别的。语句以分号结尾,没有返回值;表达式不以分号结尾,有返回值。函数的最后一个表达式,就是函数的返回值。当然,你也可以用return关键字提前返回。
4. 控制流
Rust的控制流,包括if、loop、while、for等。
fn main() {
// if表达式
let number = 7;
if number < 5 {
println!("小于5");
} else if number == 5 {
println!("等于5");
} else {
println!("大于5");
}
// if可以作为表达式
let condition = true;
let x = if condition { 5 } else { 6 };
// loop循环(无限循环,用break退出)
let mut count = 0;
loop {
count += 1;
if count == 10 {
break;
}
}
// while循环
let mut n = 0;
while n < 10 {
n += 1;
}
// for循环
for i in 1..11 { // 1到10,不包含11
println!("{}", i);
}
let arr = [10, 20, 30, 40, 50];
for element in arr.iter() {
println!("{}", element);
}
}5. 结构体和枚举
Rust支持结构体(struct)和枚举(enum)。
// 结构体
struct User {
username: String,
email: String,
age: u32,
active: bool,
}
// 枚举
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
fn main() {
let user = User {
username: String::from("alice"),
email: String::from("alice@example.com"),
age: 30,
active: true,
};
let msg = Message::Write(String::from("hello"));
}Rust的枚举非常强大,每个枚举变体可以携带不同类型的数据,这在很多场景下非常有用。
6. 模式匹配
Rust的模式匹配(match),是一个非常强大的特性,可以用来处理枚举、结构体、元组等。
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
fn process(msg: Message) {
match msg {
Message::Quit => {
println!("退出程序");
}
Message::Move { x, y } => {
println!("移动到 ({}, {})", x, y);
}
Message::Write(text) => {
println!("写入: {}", text);
}
Message::ChangeColor(r, g, b) => {
println!("改变颜色为 ({}, {}, {})", r, g, b);
}
}
}match要求穷举所有可能,也就是说,你必须处理枚举的每一个变体。这能保证你不会遗漏任何情况,减少bug。
五、Rust的核心概念:所有权、借用、生命周期
Rust最独特、也是最难理解的部分,就是它的所有权系统(Ownership)。这是Rust保证内存安全的核心机制。
1. 所有权规则
Rust的所有权,有三条基本规则:
- Rust中每个值都有一个变量,称为它的所有者(owner)。
- 同一时间,只能有一个所有者。
- 当所有者离开作用域时,这个值会被丢弃(drop),内存被释放。
fn main() {
{
let s = String::from("hello"); // s是字符串的所有者
// 使用s
} // s离开作用域,字符串被丢弃,内存释放
}2. 移动(Move)
在Rust中,把一个变量赋值给另一个变量,或者作为参数传给函数,默认是"移动",而不是"拷贝"。移动之后,原来的变量就不能再使用了。
fn main() {
let s1 = String::from("hello");
let s2 = s1; // s1的所有权移动给了s2
// println!("{}", s1); // 这行会编译错误!s1已经不能用了
println!("{}", s2); // 这行是可以的
}这个设计,保证了同一时间只有一个所有者,避免了双重释放(double free)的问题。
对于简单的、固定大小的类型(比如整数、浮点数、布尔值),Rust会自动拷贝,而不是移动,因为它们实现了Copy trait。
3. 克隆(Clone)
如果你想深拷贝一个值,而不是移动,可以用clone方法。
fn main() {
let s1 = String::from("hello");
let s2 = s1.clone(); // 深拷贝
println!("s1 = {}, s2 = {}", s1, s2); // 两个都可以用
}clone会在堆上分配新的内存,拷贝数据,所以开销比较大。只有在确实需要的时候才用。
4. 借用(Borrowing)
很多时候,我们不想转移所有权,只是想临时使用一个值。这时候,可以用"借用",也就是引用。
引用用&表示,它允许你使用一个值,但是不拥有它的所有权。
fn calculate_length(s: &String) -> usize { // s是对String的引用,不拥有所有权
s.len()
} // s离开作用域,但是因为它不拥有所有权,所以不会丢弃值
fn main() {
let s1 = String::from("hello");
let len = calculate_length(&s1); // 借用s1,传引用
println!("'{}'的长度是{}", s1, len); // s1还可以用,因为所有权没有转移
}引用默认是不可变的。如果你想通过引用修改值,需要用可变引用&mut。
fn change(s: &mut String) {
s.push_str(", world");
}
fn main() {
let mut s = String::from("hello");
change(&mut s);
println!("{}", s); // 输出 hello, world
}5. 借用规则
Rust的借用,有两条重要规则:
- 在同一时间,你可以有任意多个不可变引用,或者一个可变引用,但是不能同时有可变引用和不可变引用。
- 引用必须总是有效的(不能有悬垂引用)。
fn main() {
let mut s = String::from("hello");
let r1 = &s; // 不可变引用
let r2 = &s; // 可以有多个不可变引用
// let r3 = &mut s; // 这行会编译错误!不能同时有不可变和可变引用
println!("{}, {}", r1, r2);
// r1和r2在这里之后就不再使用了
let r3 = &mut s; // 这行是可以的,因为r1和r2已经不用了
println!("{}", r3);
}这些规则,在编译期就保证了不会有数据竞争,也不会有悬垂引用。
6. 生命周期(Lifetime)
生命周期,是Rust中另一个比较难理解的概念。简单说,生命周期就是引用的有效范围。Rust编译器会检查引用的生命周期,确保引用不会指向已经被释放的值。
大部分时候,Rust编译器能自动推断生命周期,你不需要显式标注。但是在某些情况下(比如函数返回引用),你需要显式标注生命周期。
// 显式标注生命周期:'a
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}这个函数的意思是:返回的引用的生命周期,和参数x、y的生命周期中较短的那个一样。这样,编译器就能保证返回的引用不会是悬垂引用。
生命周期是Rust中比较高级的概念,初学者刚开始可以不用太纠结,先用起来,遇到问题再深入学习。
六、学习资源和建议
最后,给大家推荐一些Rust的学习资源:
官方资源:
- 《The Rust Programming Language》(Rust官方书,俗称"the book"):https://doc.rust-lang.org/book/ ,这是学习Rust最好的入门书,免费在线阅读。
- Rust by Example:https://doc.rust-lang.org/rust-by-example/ ,通过例子学习Rust。
- Rust标准库文档:https://doc.rust-lang.org/std/
社区资源:
- Rust Reddit:https://www.reddit.com/r/rust/
- Rust用户论坛:https://users.rust-lang.org/
- 国内Rust社区:https://rust.cc/
学习建议:
- 先读the book:这是最好的入门教材,跟着书里的例子,一边读一边写代码。
- 多写代码:Rust的所有权、借用这些概念,光看书是理解不了的,必须多写代码,在编译错误中学习。
- 不要怕编译错误:Rust的编译器非常友好,错误信息很详细,还会给出修复建议。把编译错误当成老师,从错误中学习。
- 从小项目开始:学完基础之后,可以写一些小项目,比如命令行工具、简单的Web服务器,在实践中巩固。
- 看标准库源码:Rust的标准库写得非常优雅,读标准库的源码,能学到很多Rust的最佳实践。
七、写在最后
Rust,是一门很有特色的编程语言。它的所有权系统,虽然学习曲线陡峭,但是一旦理解了,你会发现它能帮你写出更安全、更可靠的代码。
到2018年,Rust已经越来越成熟,生态也越来越完善。不管你是做系统编程、Web开发,还是想学习一门新的编程语言,Rust都值得你花时间去了解和学习。
当然,Rust不是银弹,不是所有场景都适合用Rust。如果你只是写一个简单的脚本,或者快速原型,Python、JavaScript可能更合适。但是,如果你需要高性能、高安全、高并发的系统,Rust是一个非常好的选择。
最后,用一句话来结束这篇入门指南:"Rust的学习曲线虽然陡峭,但是它能让你对编程有新的认识,写出更安全、更可靠的代码。如果你对系统编程、内存安全、高性能感兴趣,Rust值得你花时间去学习。"
希望这篇文章,能帮你打开Rust的大门。祝你学习愉快!
评论(0)
暂无评论,快来抢沙发~
评论功能仅对会员开放,请先登录
登录