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")
}
}
}
七、学习资源推荐
-
实践教程
Rust 编程之道 -
社区支持
-
Rust 中文社区:https://rustlang-cn.org/
-
Rust 语言中文论坛:https://bbs.rustlang.org.cn/
-
-
在线练习
Rust Playground
八、常见问题解决
所有权错误
bash
error[E0382]: borrow of moved value
解决方案:
-
使用引用
& -
实现
Clonetrait -
重新设计数据生命周期
升级 Rust 版本
bash
rustup update
格式化代码
bash
cargo fmt # 需要安装 rustfmt
优化SO
使用 --release 模式,并在 Cargo.toml 中配置 panic = "abort" 和 opt-level = "z"(优化大小),体积会瞬间从几十 MB 缩减到几百 KB。