01 Rust快速入门
基本语法
基本数据类型
Rust 是一门静态类型语言,变量默认是不可变的(immutable)。
标量类型:
整型:
i8,i32,i64,u32(有符号与无符号)浮点型:
f32,f64布尔型:
bool(true/false)字符型:
char(表示 Unicode 标量值,占 4 个字节)复合类型:
元组(Tuple):
(i32, f64, &str),长度固定,类型可不同。数组(Array):
[i32; 3],长度固定,类型必须相同。动态数组(Vec):
Vec[i32]长度不固定,可以追加;
let x: i32 = 42; // 明确指定类型
let pi = 3.14; // 自动推导为 f64
let is_active: bool = true;
let tup: (i32, f64) = (500, 6.4);
let arr: [i32; 3] = [1, 2, 3];
String基础操作:
fn main() {
// 1. 初始化
let mut s: String = String::from("Hello");
let s_slice: &str = "World";
// 2. 拼接
// push_str 追加切片,push 追加字符,+ 操作符(注意所有权转移)
s.push_str(", ");
s.push('R');
let combined = s + s_slice; // 此时 s 的所有权已被转移
let s2 = format!("{} {}", "Hello", "Rust"); // 使用 format! 宏(不转移所有权)
// 3. 查找
let text = "rust programming";
let pos = text.find("gram"); // 返回 Option<usize>
let contains_rust = text.contains("rust"); // 返回 bool
// 4. 大小写转换
let upper = "hello".to_uppercase();
let lower = "WORLD".to_lowercase();
// 5. 相等比较
let is_equal = ("abc" == "abc");
// 6. 长度计算(注意:计算的是字节数,不是字符数)
let len = "Hello".len();
let char_count = "你好".chars().count(); // 获取真实字符数
// 7. 遍历
for c in "rust".chars() {
print!("{} ", c);
}
// 8. 转码 (UTF-8 字节与字符串互转)
let bytes: &[u8] = text.as_bytes();
let decoded = String::from_utf8(bytes.to_vec()).unwrap();
}
数组与动态数组 (Array & Vec):
fn main() {
// --- 固定大小数组 (Array) ---
// 1. 初始化
let arr: [i32; 3] = [1, 2, 3];
let zeros = [0; 5]; // 初始化为 5 个 0
// 2. 遍历
for x in arr.iter() {
println!("{}", x);
}
// --- 动态数组 (Vec) ---
// 1. 初始化
let mut vec: Vec<i32> = Vec::new();
let mut vec_macro = vec![1, 2, 3];
// 2. 插入与修改
vec_macro.push(4); // 末尾添加
vec_macro.insert(0, 99); // 指定索引插入(开销较大)
// 3. 删除
let last = vec_macro.pop(); // 弹出末尾元素
// 4. 查找与包含
let has_two = vec_macro.contains(&2);
let index = vec_macro.iter().position(|&x| x == 3); // 查找索引
// 5. 长度计算
let len = vec_macro.len();
let is_empty = vec_macro.is_empty();
// 6. 遍历
for item in &vec_macro {
println!("{}", item);
}
}
键值对映射 (HashMap):
use std::collections::HashMap;
fn main() {
// 1. 初始化
let mut scores = HashMap::new();
// 2. 插入
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);
// entry API: 键不存在时才插入
scores.entry(String::from("Green")).or_insert(30);
// 3. 查找
let score = scores.get("Blue"); // 返回 Option<&V>
// 4. 遍历
for (key, value) in &scores {
println!("{}: {}", key, value);
}
// 5. 删除与长度
scores.remove("Yellow");
let count = scores.len();
}
基本数据类型常用操作 (整型、布尔等):
fn main() {
// 1. 类型转换 (as 关键字)
let a: i32 = 10;
let b: f64 = a as f64;
// 2. 基础数学运算与溢出检查方法
let x: u32 = 100;
let checked_add = x.checked_add(1); // 返回 Option<u32>,防止溢出
let wrapping_add = x.wrapping_add(1); // 溢出时回绕
// 3. 数值与字符串互转
let num_str = "42";
let parsed_num: i32 = num_str.parse().unwrap(); // 字符串转数字
let back_to_str = parsed_num.to_string(); // 数字转字符串
}
定义函数
使用 fn 关键字。函数签名必须明确声明参数类型;如果函数有返回值,必须在箭头 -> 后指定返回类型。函数体的最后一个表达式会自动作为返回值返回(无需写 return 和分号)。
fn add(a: i32, b: i32) -> i32 {
a + b // 隐式返回,注意没有分号
}
定义结构体和方法
使用 struct 定义自定义数据结构,并通过 impl 块为其绑定关联函数和方法。
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
// 关联函数(类似于其他语言的静态方法/构造函数)
fn new(width: u32, height: u32) -> Rectangle {
Rectangle { width, height }
}
// 方法(以 &self 作为第一个参数)
fn area(&self) -> u32 {
self.width * self.height
}
}
定义宏
定义枚举值
Rust 的枚举(Enum)极其强大,不仅可以作为简单的状态标记,还可以携带各种类型的数据(元组或结构体样式)。
enum Message {
Quit, // 纯状态标记
Move { x: i32, y: i32 }, // 具名字段结构体样式
Write(String), // 携带数据的元组样式
}
定义异常值
Rust 没有传统意义上的 try-catch 异常抛出机制。错误被视为普通的返回值,主要通过两种类型处理:
Result<T, E>:用于处理可能失败的操作。包含成功Ok(T)和失败Err(E)。Option<T>:用于处理可能存在或不存在的值。包含有值Some(T)和无值None。
// 自定义错误枚举
#[derive(Debug)]
enum MathError {
NegativeSquareRoot(f64), // 携带错误现场数据的枚举成员
}
// 定义一个返回 Result 的“可能出错”的函数
fn safe_sqrt(number: f64) -> Result<f64, MathError> {
if number < 0.0 {
return Err(MathError::NegativeSquareRoot(number)); // 抛出“异常”值
}
Ok(number.sqrt())
}
// 定义一个返回 Option 的“可能为空”的函数
fn get_user_status(id: u64) -> Option<String> {
if id == 1 {
Some("Active".to_string()) // 有值时必须用 Some 包装
} else {
None // 无值时返回 None
}
}
语言特征
数据的归属借还机制
默认的数据是不可修改 只能引用可读
Rust 的核心设计哲学是内存安全与并发安全。
默认不可变:在 Rust 中,变量默认是不可变的(immutable)。一旦声明并绑定了数值,你就无法修改它。
只读引用(
&T):你可以通过借用(Borrowing)的方式获取数据的只读引用。在同一时间,你可以拥有无数个只读引用,但没有任何人可以修改这块数据。
let s = String::from("hello");
let r1 = &s; // 只读引用 1
let r2 = &s; // 只读引用 2
println!("{} and {}", r1, r2); // 合法:大家一起读
&mut 表数据借还可写
可变引用(
&mut T):如果你想修改借用的数据,必须声明为可变借用。铁律(排他性):Rust 的借用检查器(Borrow Checker)规定:在任意给定的时间,要么只能有任意数量的只读引用(
&T),要么只能有且仅有一个可变引用(&mut T)。这从根本上消除了多线程或复杂逻辑下的数据竞争(Data Race)。
let mut s = String::from("hello");
let r1 = &mut s; // 可变借用
r1.push_str(", world");
println!("{}", r1); // 合法:当前只有 r1 在操作
和 C++ 的智能指针对应理解
Rust 的所有权和借用机制,本质上是把 C++ 中靠人工规范和复杂工具去防范的内存错误,直接在编译期通过类型系统规范化了:
**
Box<T>对应 C++ 的std::unique_ptr**:独占式堆内存所有权,离开作用域自动释放。**
Rc<T> / Arc<T>对应 C++ 的std::shared_ptr**:引用计数智能指针。Rc用于单线程,Arc用于多线程(Atomic)。**
&和&mut对应 C++ 的const T&和T&**:但 Rust 的引用带有严格的生命周期和编译期借用检查,杜绝了 C++ 中常见的悬垂引用(Dangling Reference)。
以及其他的数据生命周期相关
生命周期(Lifetimes,
'a):这是 Rust 最独特的高级概念。生命周期并不是指变量活多久,而是编译器用来确保引用有效性的作用域范围。生命周期标注:当函数返回一个引用时,编译器需要知道这个引用的存活时间是否和输入的参数挂钩。如果不标注,编译器无法推导。
// 告诉编译器:返回值引用的生命周期,和参数 x、y 中较短的那一个保持一致
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
模式匹配
常见的数据包装
Rust 没有像其他语言那样隐式的空指针或非结构化异常,而是通过精妙的泛型枚举来包裹数据:
Option<T>:处理“有值”或“无值”的场景。包含Some(T)(有值)和None(无值)。Result<T, E>:处理“成功”或“失败”的场景。包含Ok(T)(成功结果)和Err(E)(错误信息)。自定义枚举包装:例如前面提到的携带数据的枚举
MathError::NegativeSquareRoot(f64)。
数据模式匹配和解包
模式匹配不仅是条件分支,更是强大的数据解构工具。
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
}
fn handle_message(msg: Message) {
match msg {
Message::Quit => println!("退出"),
// 解构结构体样式枚举,直接提取字段
Message::Move { x, y } => println!("移动到: x={}, y={}", x, y),
// 解构元组样式枚举,直接提取内部包裹的 String
Message::Write(text) => println!("写入内容: {}", text),
}
}