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?

  1. 内存安全,没有空指针和野指针:Rust在编译期就检查内存安全,很多C/C++中常见的内存错误(空指针解引用、野指针、缓冲区溢出、使用已释放内存等),在Rust中编译都通不过。这大大减少了运行时的bug和安全漏洞。
  2. 没有垃圾回收,性能高:Rust不需要垃圾回收器,通过所有权系统在编译期管理内存,运行时没有GC停顿,性能和C/C++相当。
  3. 并发安全:Rust的类型系统,能在编译期防止数据竞争,让并发编程更安全、更简单。
  4. 现代的语言特性:Rust有模式匹配、泛型、trait、迭代器、闭包等现代语言特性,表达能力强,代码优雅。
  5. 优秀的工具链:Rust有统一的包管理工具Cargo,有内置的测试框架、文档生成、格式化工具,开发体验很好。
  6. 社区活跃,生态 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_world

Cargo会自动创建项目结构:

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的所有权,有三条基本规则:

  1. Rust中每个值都有一个变量,称为它的所有者(owner)。
  2. 同一时间,只能有一个所有者。
  3. 当所有者离开作用域时,这个值会被丢弃(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的借用,有两条重要规则:

  1. 在同一时间,你可以有任意多个不可变引用,或者一个可变引用,但是不能同时有可变引用和不可变引用。
  2. 引用必须总是有效的(不能有悬垂引用)。
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/

学习建议:

  1. 先读the book:这是最好的入门教材,跟着书里的例子,一边读一边写代码。
  2. 多写代码:Rust的所有权、借用这些概念,光看书是理解不了的,必须多写代码,在编译错误中学习。
  3. 不要怕编译错误:Rust的编译器非常友好,错误信息很详细,还会给出修复建议。把编译错误当成老师,从错误中学习。
  4. 从小项目开始:学完基础之后,可以写一些小项目,比如命令行工具、简单的Web服务器,在实践中巩固。
  5. 看标准库源码:Rust的标准库写得非常优雅,读标准库的源码,能学到很多Rust的最佳实践。

七、写在最后

Rust,是一门很有特色的编程语言。它的所有权系统,虽然学习曲线陡峭,但是一旦理解了,你会发现它能帮你写出更安全、更可靠的代码。

到2018年,Rust已经越来越成熟,生态也越来越完善。不管你是做系统编程、Web开发,还是想学习一门新的编程语言,Rust都值得你花时间去了解和学习。

当然,Rust不是银弹,不是所有场景都适合用Rust。如果你只是写一个简单的脚本,或者快速原型,Python、JavaScript可能更合适。但是,如果你需要高性能、高安全、高并发的系统,Rust是一个非常好的选择。

最后,用一句话来结束这篇入门指南:"Rust的学习曲线虽然陡峭,但是它能让你对编程有新的认识,写出更安全、更可靠的代码。如果你对系统编程、内存安全、高性能感兴趣,Rust值得你花时间去学习。"

希望这篇文章,能帮你打开Rust的大门。祝你学习愉快!