Rust是一种注重安全、速度和并发的系统编程语言。它通过所有权、借用和生命周期的概念来实现内存安全,无需垃圾回收。以下是Rust的基本语法介绍,包括代码示例和说明。
1. 基本语法结构
1.1 变量和常量
在Rust中,变量默认是不可变的。要定义一个变量,使用let
关键字,如下所示:
如果需要可变变量,可以在变量名前加上mut
关键字:
1
2
|
let mut y = 10;
y = 20; // 允许修改
|
常量使用const
关键字定义,它们必须是静态的且在编译时就能确定的值:
1
|
const MAX_POINTS: u32 = 100_000;
|
1.2 数据类型
Rust是静态类型语言,每种变量都有明确的类型。Rust的基本类型包括整型、浮点型、布尔型等:
- 整型:i32, i64, u8, u32, 等。
- 浮点型:f32, f64。
- 布尔型:bool,值为true或false。
- 字符型:char,用于表示Unicode标量值。
1
2
3
|
let integer: i32 = 1;
let float: f64 = 1.3;
let boolean: bool = true;
|
类型推导是Rust的一个特性,编译器会根据变量的初始值推断其类型:
1
2
3
|
let a = 1; // 类型为i32
let b = 1.0; // 类型为f64
let c = 'a'; // 类型为char
|
1.3 复合类型
Rust支持多种复合类型,如元组、数组和结构体:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
// 元组
let tuple: (i32, f64, bool) = (1, 2.3, true);
// 数组
let array: [i32; 5] = [1, 2, 3, 4, 5];
// 结构体
struct Point {
x: f32,
y: f32,
}
let point = Point { x: 1.0, y: 2.0 };
|
结构体是创建自定义数据类型的一种方式:
1
2
3
4
5
6
7
8
9
10
|
struct Point {
x: f64,
y: f64,
}
impl Point {
fn new(x: f64, y: f64) -> Point {
Point { x, y }
}
}
|
1.4 函数
在Rust中,使用fn
关键字定义函数:
1
2
3
4
5
6
7
8
9
10
11
12
|
fn main() {
println!("Hello, world!");
}
fn add(a: i32, b: i32) -> i32 {
a + b
}
fn main() {
let sum = add(10, 20);
println!("The sum is {}", sum);
}
|
1.5 枚举
枚举是Rust中另一种自定义数据类型,可以表示一组不同的值:
1
2
3
4
5
6
|
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
|
1.6 条件语句
Rust使用if、else if和else进行条件判断:
1
2
3
4
5
6
7
8
|
let condition = true;
if condition {
println!("It's true!");
} else if condition == false {
println!("It's false, but checked again.");
} else {
println!("It's false.");
}
|
1.7 循环
Rust支持几种类型的循环:
- for循环:用于遍历序列。
- while循环:在给定条件为真时执行代码块。
- loop:无限循环,直到使用break语句退出。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
for i in 0..5 {
println!("Number is: {}", i);
}
let mut count = 0;
while count < 5 {
println!("Count is: {}", count);
count += 1;
}
loop {
println!("Infinite loop!");
break; // 退出循环
}
|
2. 所有权、借用和生命周期
这是Rust的核心概念之一,用于管理内存:
- 所有权(Ownership):每个值都有一个变量作为其所有者。
- 借用(Borrowing):可以借用数据而不取得所有权。
- 生命周期(Lifetimes):确保引用有效。
2.1 所有权
Rust的所有权规则确保了内存安全。每个值在任何时候都有一个变量,称为其所有者,或者没有所有者。
1
2
3
|
fn main() {
let s = "Hello".to_string(); // s是这个String的拥有者
}
|
2.2 借用
你可以借用数据,但借用必须满足以下条件:
- 要么是一个不可变借用,可以有任意数量,
- 要么是一个可变借用,但同时只能有一个。
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
|
fn main() {
let s = String::from("hello");
take_ownership(s); // s的所有权被转移到函数中
// println!("{}", s); // 这里s不能用了,因为所有权已经转移
let x = 5;
make_mutable(&x); // 通过不可变引用借用x
println!("{}", x); // x仍然可以使用
let mut y = 5;
change_value(&mut y); // 通过可变引用改变y的值
println!("{}", y); // y的值现在是6
}
fn take_ownership(s: String) {
// s的所有权现在在这里
}
fn make_mutable(x: &i32) {
// x是一个不可变引用,不能改变x的值
}
fn change_value(y: &mut i32) {
*y = 6; // 可变引用可以改变y指向的值
}
|
2.3 生命周期
Rust 的生命周期概念是其内存安全特性的核心部分,它确保了在任何给定时间,引用指向的数据都是有效的,从而避免了悬垂指针(dangling pointer)和其他内存安全问题。以下是Rust生命周期概念的工作原理:
2.3.1 生命周期的存在
在Rust中,每个引用都有一个生命周期,这是引用有效存在的时间段。生命周期确保引用在数据的整个生命周期内都是有效的。
2.3.2 生命周期注解
在某些情况下,Rust编译器可能无法自动推断引用的生命周期,这时需要开发者显式地指定生命周期注解。例如:
1
2
3
4
5
6
7
|
fn longest<'a>(s1: &'a str, s2: &'a str) -> &'a str {
if s1.len() > s2.len() {
s1
} else {
s2
}
}
|
在这个例子中,'a
是一个生命周期参数,表明s1
和s2
的引用都拥有相同的生命周期'a
,函数返回的引用也拥有这个生命周期。
2.3.3 借用规则
Rust的借用规则规定,在任何给定时间,要么只有不可变引用(&T
),要么只有一个可变引用(&mut T
),但不能同时存在。这是为了避免数据竞争。
2.3.4 结构体和生命周期
当结构体包含引用时,需要为结构体定义生命周期,以确保引用指向的数据在结构体的生命周期内都是有效的:
1
2
3
4
5
6
7
8
9
|
struct Person<'a> {
name: &'a str,
age: u32,
}
fn main() {
let name = "John Doe";
let person = Person { name: &name, age: 30 };
}
|
2.3.5 生命周期的消歧(Lifetime Elision)
Rust编译器在很多情况下可以自动推导生命周期,这个过程称为生命周期消歧。它基于三个基本规则:
- 每个引用的生命周期都与某个输入生命周期相关联。
- 如果多个引用都指向同一个变量,它们可以共享一个生命周期。
- 如果一个返回值是一个引用,这个引用的生命周期应该与一个输入参数的生命周期相同。
2.3.6 静态生命周期(‘static)
'static
生命周期表示引用的生命周期与整个程序的生命周期一样长,通常用于指向字符串字面量或全局静态数据的引用。
2.3.7 生命周期和迭代器
在迭代器中,生命周期的概念尤为重要,因为迭代器的.next()
方法返回的是一个对集合中元素的引用:
1
2
3
4
5
6
7
|
fn main() {
let v = vec![1, 2, 3];
let mut iter = v.iter();
while let Some(&item) = iter.next() {
println!("{}", item);
}
}
|
在这个例子中,iter
迭代器的生命周期与v
向量的生命周期相关联。
2.3.8 高级生命周期
在更复杂的情况下,可能需要使用高级生命周期注解,如生命周期界限(lifetime bounds)等,来解决生命周期的复杂关系。
Rust的生命周期概念是确保内存安全的关键机制之一。通过生命周期注解和编译器的自动推断,Rust能够有效地管理引用的生命周期,防止悬垂指针和其他内存错误。这对于编写安全、可靠的系统级代码尤为重要。
3. 错误处理
Rust使用Result
类型来处理可能发生的错误。Result
是一个枚举,有两个变体:Ok
和Err
。
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
|
fn main() {
let result = func();
match result {
Ok(value) => println!("Got a value: {}", value),
Err(e) => println!("An error occurred: {}", e),
}
let result2 = divide(10, 2);
match result2 {
Ok(value) => println!("The result is {}", value),
Err(e) => println!("Error: {}", e),
}
}
fn func() -> Result<i32, &'static str> {
// 模拟可能失败的操作
if true {
Ok(10)
} else {
Err("something went wrong")
}
}
fn divide(x: i32, y: i32) -> Result<i32, String> {
if y == 0 {
Err(String::from("Cannot divide by zero"))
} else {
Ok(x / y)
}
}
|
4. 泛型和特性
4.1 泛型
泛型允许你定义函数和类型,它们可以适用于不同的数据类型。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
fn largest<T: PartialOrd>(list: &[T]) -> T {
let mut largest = list[0];
for &item in list.iter() {
if item > largest {
largest = item;
}
}
largest
}
fn main() {
let numbers = vec![34, 50, 25, 100, 65];
let result = largest(&numbers);
println!("The largest number is {}", result);
}
|
4.2 特性(Traits)
特性类似于接口,定义了一组方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
trait Animal {
fn make_sound(&self);
}
struct Dog;
impl Animal for Dog {
fn make_sound(&self) {
println!("Woof!");
}
}
fn main() {
let dog = Dog;
dog.make_sound();
}
|
5. 模块和包
5.1 模块
模块用于组织代码。
1
2
3
4
5
6
7
8
9
|
mod math {
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
}
fn main() {
println!("The answer is {}", math::add(10, 20));
}
|
5.2 包
包是模块的集合,通常对应于一个crate。使用cargo new
创建新包。
1
2
3
4
5
|
# Cargo.toml
[package]
name = "my_package"
version = "0.1.0"
authors = ["Your Name <yourname@example.com>"]
|
6. 并发编程
6.1 线程
Rust的std::thread
模块提供了多线程支持。
1
2
3
4
5
6
7
8
9
|
use std::thread;
fn main() {
let child = thread::spawn(|| {
println!("Hello from a thread!");
});
child.join().unwrap();
}
|
6.2 消息传递
Rust支持线程间的消息传递。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
use std::sync::mpsc;
use std::thread;
fn main() {
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
let val = mpsc::RecvError::into_inner(1);
tx.send(val).unwrap();
});
let received = rx.recv().unwrap();
println!("Received: {}", received);
}
|
7. 智能指针
Rust提供了多种智能指针,如Box
、Rc
、Arc
和RefCell
等。
1
2
3
4
5
6
7
8
9
10
11
|
use std::rc::Rc;
use std::cell::RefCell;
let a = Rc::new(5);
{
let b = Rc::clone(&a);
let c = RefCell::new(a);
} // b和c离开作用域,但a仍然有效
println!("Rc count: {}", Rc::strong_count(&a));
|
8. 模式匹配
Rust的match
语句是一种强大的模式匹配工具。
1
2
3
4
5
6
7
8
9
10
11
|
enum Color {
Rgb(i32, i32, i32),
Hsv(i32, i32, i32),
}
let color = Color::Rgb(255, 0, 255);
match color {
Color::Rgb(r, g, b) => println!("RGB:({}, {}, {})", r, g, b),
Color::Hsv(h, s, v) => println!("HSV:({}, {}, {})", h, s, v),
}
|
9. 宏
宏是Rust的强大特性,允许你编写更灵活的代码。
1
2
3
4
5
6
7
8
9
|
macro_rules! print_result {
($expression:expr) => {
println!("The result is: {}", $expression);
};
}
fn main() {
print_result!(2 + 2);
}
|
10. 测试
Rust提供了内置的测试支持。
1
2
3
4
5
6
7
|
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
}
|
11. 错误处理和迭代器
Rust的迭代器和闭包提供了强大的数据处理能力。
1
2
3
4
5
6
7
8
9
|
fn main() {
let v = vec![1, 2, 3, 4, 5];
let iter = v.iter().map(|x| x * 2).filter(|x| *x > 3);
for val in iter {
println!("{}", val);
}
}
|
12. 格式化输出
Rust的format!
和println!
宏允许你格式化输出。
1
2
3
4
5
6
7
|
fn main() {
let name = "Rustacean";
let amount = 5;
let price = 10.99;
println!("{name} buys {amount} apples at ${price} each.", name=name, amount=amount, price=price);
}
|
13. 模式匹配和解构
Rust允许你使用模式匹配来解构数据结构。
1
2
3
4
|
fn main() {
let (a, b, c) = (1, 2, 3);
println!("a: {}, b: {}, c: {}", a, b, c);
}
|
14. 特性(Traits)和生命周期
Rust的特性和生命周期是确保内存安全的关键。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
trait Shape {
fn area(&self) -> f64;
}
struct Circle {
radius: f64,
}
impl Shape for Circle {
fn area(&self) -> f64 {
std::f64::consts::PI * self.radius * self.radius
}
}
fn print_area<T: Shape>(shape: &T) {
println!("The area is {}", shape.area());
}
fn main() {
let c = Circle { radius: 2.0 };
print_area(&c);
}
|
结语
Rust的基本语法是构建可靠和高效软件的基础。通过所有权、借用和生命周期的概念,Rust提供了内存安全保证,同时保持了高性能。Rust的语法丰富,支持泛型、特性、模式匹配等高级编程特性,使其成为系统编程的强大工具。