Rust 初体验与快速上手指南

Rust 初体验与快速上手指南

一、Rust?

Rust 是一门现代系统级编程语言,兼顾性能与安全:

  • 内存安全:通过所有权(Ownership)和借用(Borrowing)机制消除空指针、数据竞争等常见错误

  • 零成本抽象:高性能的抽象能力,接近 C/C++ 的运行效率

  • 跨平台:支持主流操作系统和架构

  • 包管理器 Cargo:集成依赖管理、构建、测试、文档生成等功能


二、快速开始

1. 安装 Rust

bash 复制代码
# 使用 rustup 安装(推荐)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# 验证安装
rustc --version
cargo --version

2. 或使用 RustRover IDE

下载链接:https://www.jetbrains.com.cn/rust/download

下载安装后会自动检测rustc工具链,或直接在IDE中安装。

3.课程


三、基础语法速览

1. 变量与可变性

rust 复制代码
let x = 5;          // 不可变变量
let mut y = 10;     // 可变变量
y = 15;

// 常量
const MAX_POINTS: u32 = 100_000;

2. 数据类型

rust 复制代码
// 标量类型
let a = 9.3;        // f64
let b: bool = true;
let c = '😊';       // char(4字节 Unicode)

// 复合类型
let tup = (500, 6.4, false);
let arr = [1, 2, 3];
let arr_fixed: [i32; 5] = [0; 5];  // [0,0,0,0,0]

3. 控制流

rust 复制代码
// if 表达式
let n = if a > 5 { 10 } else { 0 };

// loop 循环
loop {
    break;  // 退出循环
}

// while 循环
while number != 0 {
    number -= 1;
}

// for 遍历
for element in arr.iter() {
    println!("{}", element);
}

4. 函数定义

rust 复制代码
fn add(x: i32, y: i32) -> i32 {
    x + y     // 隐式返回(无分号)
}

// 元组返回值
fn divide(dividend: f64, divisor: f64) -> (f64, bool) {
    (dividend / divisor, divisor != 0)
}

四、项目结构与依赖管理

1. Cargo 项目结构

bash 复制代码
rust1/
├── Cargo.toml      # 项目配置
└── src/
    ├── main.rs     # 可执行程序入口
    └── lib.rs      # 库文件(可选)

2. 添加依赖

toml 复制代码
# Cargo.toml
[dependencies]
serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1", features = ["full"] }
bash 复制代码
# 自动下载并构建依赖
cargo build

五、核心特性

宏 Macro

简单来说,宏就是"代码的魔术师",类似于Kotlin inline:你写下一行宏代码,它在程序编译之前,会根据你定义的规则自动"膨胀"成一大堆复杂的代码。想象你定义了一个叫 say_hello! 的宏。当你写 say_hello!() 时,宏会在编译前自动把它替换成 println!("Hello World")代码,而不是函数调用。

声明宏: println! say! vec!

rust 复制代码
macro_rules! my_print {
    // 匹配没有参数的情况
    () => {
        println!("你调用了一个空的宏!");
    };
    // 匹配带一个参数的情况
    ($val:expr) => {
        println!("你输入的值是: {}", $val);
    };
}

fn main() {
    my_print!();        // 输出:你调用了一个空的宏!
    my_print!(100);     // 输出:你输入的值是: 100
}

过程宏: #[derive(Debug) #[derive(MyHelper)] #[no_mangle]

可以自动生成代码,如toString之类的,可以自定义。这里不展开。

语法严格

  • **x**:非引用(原稿,借用了就没了,所有权转移)
  • **&T**:不可变借用(你可以看,但不能改)

  • **&mut T**:可变借用(你可以看,也可以改,但在借给你期间,别人连看都不能看)

没有Null

在 Rust 里,只要代码能编译通过,空指针崩溃就100%不可能发生!

Option: Some(T) None

rust 复制代码
fn check_id(id: Option<i32>) {
    match id {
        Some(value) => println!("找到了 ID: {}", value),
        None => println!("ID 不存在!"),
    }
}

// 或者更简洁的写法
if let Some(value) = id {
    println!("快速拿值: {}", value);
}

所有权

栈(stack):后进先出,已知大小范围的数据,(整数、浮点数、布尔等简单类型)

rust 复制代码
let x = 5;
let y = x; //拷贝了x的副本

// 此时:x y 都是5,但:内存地址不一样,它们分别占用栈(Stack)上不同的位置。

堆(heap):内存分配,大小可能随时变化,如String

rust 复制代码
let s1 = String::from("hello"); // 栈里存s1是指针,堆里存 hello
let s2 = s1; // 所有权发生了"移动"(Move)栈里存s2是指针,堆里存s1指针

// println!("{}", s1); // ❌ 报错!s1 已经失效了
println!("{}", s2);    // ✅ 正常执行

作用域 {},函数执行完后自动清理内存,drop

rust 复制代码
struct MyData(String);

impl Drop for MyData {
    fn drop(&mut self) {
        println!("[清理] 数据 '{}' 已被自动销毁!", self.0);
    }
}

fn main() {
    {
        // 使用 Box 将数据放在堆上
        let _smart_ptr = Box::new(MyData(String::from("Hello")));
        println!("指针正在作用域内...");
    } // 这里作用域结束,_smart_ptr 自动调用 drop,无需手动 free
    
    println!("作用域外,内存已安全释放。");
}

错误处理 Result

rust 复制代码
use std::fs;
use std::fs::File;
use std::io::{self, Read}; // 必须引入 Read 特征 才能调用 read_to_string

fn read_file() -> Result<String, io::Error> {
    // 1. 打开文件,'?' 会在出错时提前返回 Error
    let mut f = File::open("hello.txt")?;

    // 2. 准备一个空的字符串缓冲区
    let mut content = String::new();

    // 3. 将文件内容读取到字符串中
    // 注意:read_to_string 是 Read trait 的方法,需要 &mut self
    f.read_to_string(&mut content)?;

    // 4. 返回成功的 Result
    Ok(content)
}

fn read_file2() -> Result<String, io::Error> {
    fs::read_to_string("hello.txt")
}

fn main() {
    // 使用 match 处理结果
    match read_file() {
        Ok(content) => println!("{}", content), // 成功分支
        Err(e) => eprintln!("读取失败: {}", e), // 失败分支,建议用 eprintln! 输出错误流
    }
}

面向对象?

对象:结构体+impl行为;✅

封装:公开接口;✅

多态(继承):❌ 组合优于继承,特征Trait

rust 复制代码
pub trait Interface {
    fn add(&mut self) -> String;
}

并发编程

rust 复制代码
use std::thread;
use std::sync::mpsc;

let (tx, rx) = mpsc::channel();

thread::spawn(move || {
    tx.send("Rust is awesome!".to_string()).unwrap();
});

println!("Received: {}", rx.recv().unwrap());

胖瘦指针

rust 复制代码
let x: i32 = 10;
let skinny = &x; // 瘦指针:只存 x 的地址  8个字节

let array = [1, 2, 3, 4, 5];
let pointer_simple: *const i32 = &array[0]; // 普通指针:只存地址

// 通常是 16 字节(地址 8 字节 + 元数据 8 字节)
let slice_fat: &[i32] = &array[1..4];      // 胖指针:存了地址 + 长度(3)


fn main() {
    // 1. 瘦指针
    let simple_int = 5;
    let skinny_ptr = &simple_int;
    
    // 2. 胖指针 (切片)
    let array = [1, 2, 3, 4, 5];
    let fat_ptr_slice = &array[1..4]; // 指向 [2, 3, 4]
    
    // 3. 打印大小
    println!("瘦指针 (&i32) 大小: {} 字节", size_of_val(&skinny_ptr));      // 8
    println!("胖指针 (&[i32]) 大小: {} 字节", size_of_val(&fat_ptr_slice));  // 16
}

数据大小固定,指针就是瘦的; 数据动态长度,指针就是胖的。


裸指针

单一地址(unsafe) 8个字节,常用于FFI/JNI与C/C++交互

智能指针

rust 复制代码
let b = Box::new(5);  // 堆内存分配

// 多重引用
use std::rc::Rc;
let a = Rc::new("data");
let b = a.clone();    // 增加引用计数

普通指针vs智能指针

智能指针不仅是一个内存地址,它还拥有数据的所有权,并且自带"特殊技能"(比如自动释放内存、引用计数等)。

特征 普通指针/引用 (&T) 智能指针 (如 Box<T>, Rc<T>)
所有权 只是借用,不拥有数据 拥有数据的所有权
能力 仅指向内存 能自动管理生命周期(Drop)
底层结构 瘦指针/胖指针 通常是包含地址和元数据的结构体

"普通指针是'我借你的地址用一下';智能指针是'我拿着这个地址,而且我会负责在用完时把它销毁'。

六、实战项目

JNI编程
shell 复制代码
# 安装NDK
cargo install cargo-ndk

# 为rust添加目标框架
rustup target install aarch64-linux-android

# 列出所有框架
rustup target list
toml 复制代码
[lib]
crate-type = ["cdylib"]  # 这会生成动态库(.so 文件)

[dependencies]
jni = "0.21" # 自动处理 JNI 类型转换

[package.metadata.android]  # Android 特定配置
toolchain = "aarch64-linux-android"  # ARM64 架构工具链
shell 复制代码
cargo ndk build --target aarch64-linux-android --release
rust 复制代码
#[unsafe(no_mangle)] // 不要混淆函数名
pub extern "system" fn Java_com_test_rust_RustInterface_addNumbers(
    // 每一个 JNI 函数都必须包含这两个参数
    _env: JNIEnv,
    _class: JClass,
    // 以下是实际传入的参数
    a: jint,
    b: jint,
) -> jint {
   a + b
}


// 使用宏之后
// 定义宏:jni_fn!(函数名, 参数, 返回值, 逻辑)
macro_rules! jni_fn {
    ($name:ident($($arg:ident: $typ:ty),*) -> $ret:ty $body:block) => {
        paste::paste! { // 使用 paste! 宏包裹来处理名字拼接
            #[unsafe(no_mangle)]
            pub extern "system" fn [<Java_com_test_rust_RustInterface_$name>](
                _env: jni::JNIEnv,
                _class: jni::objects::JClass,
                $($arg: $typ),*
            ) -> $ret
            $body
        }
    };
}

jni_fn! {
    addNumbers(a: jint, b: jint) -> jint {
        a + b
    }
}

代码看起来像这样:

kotlin 复制代码
package com.test.rust

class RustInterface {
    // 声明 native 方法,名字必须与 Rust 中的后缀一致
    external fun addNumber(a: Int, b: Int): Int

    fun testAdd() {
      println(addNumber(1, 5))
    }

    companion object {
        init {
            // 加载库文件,库名通常是在 Cargo.toml 中定义的项目名
            System.loadLibrary("so2")
        }
    }
}

七、学习资源推荐

  1. 官方文档
    The Rust Programming Language 中文版

  2. 实践教程
    Rust 编程之道

  3. 社区支持

  4. 在线练习
    Rust Playground


八、常见问题解决

所有权错误

bash 复制代码
error[E0382]: borrow of moved value

解决方案:

  • 使用引用 &

  • 实现 Clone trait

  • 重新设计数据生命周期

升级 Rust 版本

bash 复制代码
rustup update

格式化代码

bash 复制代码
cargo fmt  # 需要安装 rustfmt

优化SO

使用 --release 模式,并在 Cargo.toml 中配置 panic = "abort"opt-level = "z"(优化大小),体积会瞬间从几十 MB 缩减到几百 KB。


相关推荐
CheungChunChiu2 小时前
Linux 内核动态打印机制详解
android·linux·服务器·前端·ubuntu
aidou13144 小时前
Android中设置Dialog和自定义布局相同高度
android·dialog·弹窗高度·getwindow
氦客4 小时前
UI编程的发展史 : 结合命令式UI和声明式UI
android·compose·声明式ui·ui编程·命令式ui·ui编程发展史·标记语言
tang777894 小时前
爬虫如何绕过绕过“5秒盾”Cloudflare:从浏览器指纹模拟到Rust求解之不完全指南
开发语言·爬虫·rust·cloudflare
Yuer20254 小时前
什么是 Rust 语境下的“量化算子”——一个工程对象的最小定义
开发语言·后端·rust·edca os·可控ai
aidou13146 小时前
Android中RecyclerView实现多级列表
android·recyclerview·多级列表·layoutmanager
青风行6 小时前
Android从入门到进阶
android
方白羽7 小时前
Android 开发中,准确判断应用处于“前台(Foreground)”还是“后台(Background)
android·app·客户端
古城小栈7 小时前
Rust 交叉编译:Windows ====> Linux (musl 静态编译)
linux·windows·rust