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 异常抛出机制。错误被视为普通的返回值,主要通过两种类型处理:

  1. Result<T, E>:用于处理可能失败的操作。包含成功 Ok(T) 和失败 Err(E)。

  2. 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),
    }
}