【硬核干货】Rust 基础入门全解:从所有权到并发编程一篇吃透

基本类型

let x = 5;
let mut y = 5;
y += 1;

let a: i32 = -10;
let b: u64 = 100;
let c: f64 = 3.14;
let d: bool = true;
let e: &str = "hello"; 
let f: String = String::from("hello");

fn add(a:i32, b:i32) -> i32 {
    a + b
}

async fn fetch() -> String {
    "data".to_string()
}

let msg = if x > 0 { "正数" } else { "非正数" }

for i in 0..5 { println!("{}", i); }
while x > 0 { x -= 1; }

match x {
    1 => println!("一"),
    2 | 3 => println!("二或三"),
    _ => println!("其它"),
}

所有权

介绍: 每个值都有且只有一个所有者,所有者离开作用域时值被自动释放(无需GC)。

Rule:

  • 每个值有且只有一个所有者
  • 同一时间只能有一个所有者
  • 所有者离开作用域,值被释放

Move 移动

let s1 = String::from("hi");
let s2 = s1;

// s1 -> s2 s1已经被移动
println!("{}", s1); // 报错

// - or
let s1 = String::from("hi"); // 堆上数值,需要进行拷贝才能使用
let s2 = s1.clone();

println!("{}", s1);
println!("{}", s2);

Copy 类型

let x = 5;
let y = x;
println!("{}", x);

借用

介绍: 用 &借用,值的所有权保留在原处。分为不可变借用和可变借用。

Rule:

  • 同时可以有多个不可变借用
  • 同时只能有一个可变借用
  • 不可变借用存在期间,不能有可变借用

不可变借用 &T

fn print_len(s: &String) {
    println!("长度: {}", s.len());
}

let s = String::from("hello");
print_len(&s); // 传入引用
println!("{}", s);

可变借用 &mut T

let mut s = String::from("hi");
{
    let r1 = &mut s;
    r1.push_str(" word");
}
let r2 = &mut s; // OK

Struct

// Struct - 自定义数据结构
struct User {
    name: String,
    age: u32,
    active: bool,
}

let user = User {
    name: String::from("Alice"),
    age: 30,
    active: true,
};

// impl - 为Struct实现办法
impl User {
    fn new(name: &str, age: u32) -> Self {
        User { name: name.to_string(), age, active: true }
    }
    fn greet(&self) -> String {
        format!("Hi, I'm {}!", self.name)
    }
    fn deactivate(&mut self) {
        self.active = false;
    }
}

let mut u = User::new("Bob", 25);
println!("{}", u.greet());
u.deactivate();

Trait

介绍: Trait定义一组方法签名,任何类型都可以实现它。类似Interface的概念

trait Greet {
  fn hello(&self) -> String;
  fn goodbye(&self) -> String {
      format!("Bye from {}", self.hello())
  }
}

struct Dog { name: String }
struct Cat { name: String }

impl Greet for Dog {
    fn hello(&self) -> String {
        format!("Woof! I'm {}", self.name)
    }
}

impl Greet for Cat {
    fn hello(&self) -> String {
        format!("Meow~ I'm {}", self.name)
    }
}

// - Trait 作为参数
fn make_greet(animal: &impl Greet) {
    println!("{}", animal.hello());
}

fn make_greet(animal: &T) {
    println!("{}", animal.hello());
}

make_greet(&Dog { name: "Rex".to_string() });
make_greet(&Cat { name: "Kitty".to_string() });

枚举 & Option

enum Direction {
    Up,
    Down,
    Left,
    Right,
}
enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
}

// - Option 可选值
// 标准库的定义
enum Option<T> {
  Some(T), // 有值
  None,
}

fn find_user(id: u32) -> Option<String> {
    if id == 1 { Some("Alivce".to_string()) }
    else { None }
}

match find_user(1) {
    Some(name) => println!("Finded: {}", name),
    None => println!("Not Found"),
}

let name = find_user(1).unwrap_or("匿名".to_string());

Result & ?

介绍: Result表示可能失败的操作

// 标准库定义
enum Result<T, E> {
    Ok(T), // 成功,带回返回值
    Err(E), // 失败,带错误信息
}

fn divide(a: f64, b: f64) -> Result<f64, String> {
    if b == 0.0 {
        Err("除数不能为0".to_string())
    } else {
        Ok(a / b)
    }
}

// - ? 链式错误传播 如果是Ok就取值,如果是Err就立刻return Err
fn run() -> Result<(), String> {
  let a = strp1()?;
  let b = step2(a)?;
  let c = step3(b)?;
  Ok(())
}

生命周期

介绍: 生命周期确保引用不会比它只想的值活得更长,防止悬空引用。大多数情况编译器自动推断,只有在引用关系复杂是才需要手动标注。

// 编译器不知道我返回的引用来自x还是y所以你需要告诉他
fn longest<'a>(x: &'a str, y &'a str) -> &'a str {
    // 声明生命周期参数表示返回值的生命周期与X和y中较短的那个相同
    if x.len() > y.len() { x } else { y }
}

// - Struct 中的生命周期
struct Excerpt<'a> {
    text: &'a str, // text 的生命周期不能超过Excerpt实例
}

let novel = String::from("Call me Ishmael...");
let excerpt = Excerpt { text: &novel[..4] };
// novel 必须比excerpt活得长,否则编译报错

建议:

  • 遇到生命周期报错,先尝试String代替 &str 持有数据
  • 用 Arc/Clone绕开复杂的生命周期
  • 能让编辑器自动推断就不要手动标注

属性 & 宏

介绍: 以#[…]形式卸写在代码上方,告诉编译器或框架做额外的事情

// - 常见内置属性
#[derive(Debug, Clone, PartialEq)]
// 自动生成 Debug/Clone等trait的实现
struct User { name: String, age: u32 }

#[test]
fn test_add() { assert_eq!(1 + 1, 2); }

#[allow(dead_code)] // 关闭"未使用代码”警告
#[cfg(target_os = "linux")] // 条件编译

// tokio属性宏,把main变成异步入口
#[tokio::main]
async fn main() { ... }

// axum的路由属性(如果用axum macros)
#[axum::debug_handler]
async fn my_handler() { ... }

// - 宏(Macro) - 代码生成代码
// Rust的宏在编译期展开,生成代码

// 声明宏(以!结尾调用)
println!("hello {}", name); // 格式化打印
vec![1, 2, 3]; // 创建Vec
format!("hi {}", x); // 格式化字符串
panic!("出错了"); // 程序崩溃
assert_eq!(a, b); // 断言相等
todo!(); // 标记未实现

// 过程宏
#[derive(serde::Seialize, serde::Deserialize)]
struct User { name: String }

// json! 宏(serde_json)
let v = json!({ "name": "Alice", "age": 30 });

闭包

介绍: 用 | 参数 | 表达式定义,可以捕获外部变量

// 普通函数
fn add_one(x: i32) -> i32 { x + 1 }

// 等价闭包
let add_one = |x| x + 1;
let add_one = |x: i32| -> i32 { x + 1 };

println!("{}", add_one(5__;

// 捕获外部变量
let offset = 10;
let add_offset = |x| x + offset;
println!("{}", add_offset(5));

// - 三种捕获方式
let s = String::from("hello");

// 不可变借用(默认)
let print_s = || println!("{}", s);
print_s();

// 可变借用
let mut count = 0;
let mut inc = || { count += 1; };
inc();

// move: 转移所有权(常用于线程、async)
let handle = std::thread::spawn(move || {
    println!("{}", s); // s 被移入闭包
});

泛型

介绍: 用<T>表示类型参数,编译时由编译器推断或填入具体类型,零运行时开销。

// - 泛型介绍
// 无泛型需要重复写函数
fn largest_i32(list: &[i32]) -> i32 { ... }
fn largest_f64(list: &[f64]) -> f64 { ... }

// 泛型写法
fn largest(list: &[T]) -> &T {
    let mut largest = &list[0];
    for item in list {
        if item > largest { largest = item; }
    }
    largest
}

largest(&[1, 5, 3, 2]);
largest(&[1.1, 5.5, 3.3]);

// 泛型Struct
struct Pair {
    first: T,
    second: T,
}

struct Map {
    key: K,
    value: V,
}

// Trait约束 复杂约束时用where更清晰
fn print_pair(pair: &Pair)
where:
    T: std::fmt::Display + Clone,
{
    println!("{} {}", pair.first, pair.second);
}

迭代器

介绍: Rust的迭代器时惰性的,链式调用不会立即执行,最终collect() 或 for 才触发计算。

let nums = vec![1, 2, 3, 4, 5];

// map -变换每个元素
let doubled: Vec = nums.iter()
    .map(|x| x * 2)
    .collect(); // [2, 4, 6, 8, 10]

// filter - 过滤
let evens: Vec<&i32> = nums.iter()
    .filter(|x| *x % 2 == 0)
    .collect(); // [2, 4]

// 链式组合
let result: Vec = nums.iter()
    .filter(|x| *x % 2 == 0)
    .map(|x| x * 10)
    .collect(); // [20, 40]

// fold - 聚合(类似reduce)
let sum = nums.iter().fold(0, |acc, x| acc + x);

// sum/count/max/min
let total: i32 = nums.iter().sum(); // 15
let max = num.iter().max(); // Some(5)

// 对比 iter/iter_mut/into_iter 的区别
let v = vec![1, 2, 3];

v.iter() // 产生 &T 借用,v仍可用
v.iter_mut() // 产生 &mut T 可变借用,可修改元素
v.into_iter() // 产生 T,消耗 v (所有权转移)

智能指针

介绍: Rust 没有GC,通过智能指针管理堆内存和共享所有权,常见有四种。

// - Box<T> - 把值放在堆上
// 用途1: 递归类型(大小在编译时未知)
enum List {
    Cons(i32, Box),
    Nil,
}

// 用途2:大对象避免栈上复制
let bit = Box::new([0u8; 1024 * 1024]);

// - Arc<T> 多线程共享所有权
use std::sync::Arc;

let data = Arc::new(vec![1, 2, 3]);
let data2 = Arc::clone(&data); // 相同数据
std::thread::spawn(move || {
    println!("{:?}", data2); // 安全的在另一个线程使用
});

// axum 里共享 AppState
let state = Arc::new(AppState { db: pool });
Router::new().with_state(state);

// - Mutex<T> -多线程安全修改
use std::sync::{Arc, Mutex};

let counter = Arc::new(Mutex::new(0));
let c = Arc::clone(&counter);
std::thread:spwan(move || {
    let mut num = c.lock().unwrap(); // 加锁
    *num += 1;
}); // 除作用域自动解锁

// - Rc<T> 单线程多所有者,引用计数
// Rc -> Referece Counted,引用计数。多个变量可以共同拥有同一份数据,最后一个所有者离开时数据才被释放
use std::rc::Rc;
let a = Rc::new(String::from("hello"));
let b = Rc::clone(&a);
let c = Rc::clone(&a);

println!("引用数: {}", Rc::strong_count(&a)); // 3
println!("{}", a); // hello
println!("{}", b); // hello

// 选择原则
// - 单线程(同一线程内共享) -> Rc(更轻量)
use std::rc::Rc;
let shared = Rc::new(data);

// - 多线程(跨线程共享)-> Arc (原子操作,有额外开销)
use std::sync::Arc;
let shared = Arc::new(data);

// - 内部可变性 RefCell<T>
// 搭配Rc使用,在运行时而非编译时检查借用规则,让不可变引用内部可修改
use std::rc::Rc;
use std::cell::RefCell;

let shared = Rc::new(RefCell::new(vec![1, 2, 3]));
let a = Rc::clone(&shared);
let b = Rc::clone(&shared);

a.borrow_mut().push(4); // 运行时借用检查
println!("{:?}", b.borrow()); // [1, 2, 3, 4]

模块系统

介绍: 用mod声明模块,pub控制可见性,use引入路径

// - 可见性规则
mod app {
    pub mod router {
        pub fn create_router() { ... }
        fn internal() { ... }
    }
    pub(crate) fn helper() { ... }
    // pub(crate) = 只在当前crate内可见
}

// 使用
use app::router::crate_router;
app::router::create_router(); // 全路径也支持

// - 文件既模块
/*
src/
 |--- main.rs <- bin crate root
 |--- lib.rs <- lib crate root
 |--- app/
     |--- mod.rs <- app 模块入口
     |--- router.rs
     |--- serve.rs
*/

// lib.rs中声明
pub mod app; // -> app/mod.rs

// app/mod.rs中声明
pub mod router; // -> app/router.rs
pub mod serve; // -> app/serve.rs

// main.rs使用lib里的内容
use <pkg_name>::app::router::create_router;

// - 常用use技巧
// 批量引入
use std::collections::{HashMap, HashSet};
// 别名
use std::fmd::Result as FmtResult;
// 引入所有公开内容(谨慎使用)
use std::io::prelude::*;
//重新导入(让外部可以通过crate访问)
pub use self::router::create_rotuer;

async/await

介绍: Rust的async是零成本抽象,编译成状态机,不需要运行时线程。需要一个运行时(tokio)来驱动执行。

// - Future - async的本质
// async fn 实际上返回一个impl Future
async fn fetch_data() -> String {
    "data".to_string()
}

// 等价于:
fn fetch_data() -> impl Future {
    async { "data".to_string() }
}

// Future是懒的,必须被.await才会执行
let fut = fetch_data(); // 什么都不会发生
let data = fut.await; // 现在才执行

// - 并发执行多个任务
use tokio::join;

// 串行
let a = task_one().await;
let b = task_two().await;

// 并发
let (a, b) = join!(task_one(), task_two());

// spawn: 放在后台独立运行
tokio::spawn(async {
    background_job().await
});

// 等待第一个完成的任务
tokio::select! {
    result = task_one() => println!("one: {:?}", result),
    result = task_two() => println!("two: {:?}", result),
}

// - 常见错误:在async中用阻塞操作
async fn handler() {
    let f = stf::fs::read("file.txt"); // Wrong
    let f = tokio::fs::read("file.txt")
        .await
        .unwrap();
}

dyn trait

介绍: dyn trait动态分发(运行时多态),与泛型impl Trait(编译器确定类型)不同,dyn Trait在运行时才确定具体类型,嗯可以把不同类型放进同一个集合。

// - 静态分发
fn make_sound(a: &impl Animal) {
    a.sound();
}

make_sound(&Dog{});
make_sound(&Cat{});
// - 动态分发
fn make_sound(a: &dyn Animal) {
    a.sound();
}
let animals: Vec> = vec![Box::new(Dog{}),
                         Box::new(Cat{})];

模式匹配

介绍:match很强大,但是有点啰嗦,if let 是值匹配一种情况的语法糖

// - if let
let opt = Some(42);
if let Some(v) = opt {
    println!("{}", v);
}

// 带else
if let Some(v) = opt {
    println!("有值: {}", v);
} else {
    println!("没有值");
}

// - 解构
// 解构struct
let user = User { name: "Alice".to_string(), age: 30 };
let User { name, age } = user;
println("{} is {}", name, age);

// 解构 tuple
let (x, y, z) = (1, 2, 3);

// 解构枚举
match msg {
    Message::Move { x, y } => println!("move to {}, {}", x, u),
    Message::Write(text) => println!("write: {}", text),
    Message::Quit => println!("quit"),
}

// 解构函数参数
fn print_point(&(x, y), &(i32, i32)) {
    println!("({}, {})", x, y);
}

// - while let/matches!宏
// while let: 有值就继续循环
let mut stack = vec![1, 2, 3];
while let Some(top) = stack.pop() {
    println!("{}", top); // 3, 2, 1
}

// matched! 宏:快速布尔判断
let x = Some(5);
let is_some = matches!(x, Some(_)); // true
let is_big = matches!(x, Some(n) if n > 3); // true

类型转换

介绍:From/Into类型转换Trait,Rust不做隐式类型转换,所有转换必须显式。From和Into是标准化转换接口,实现一个自动得到另一个。

// - 基本用法
struct Meters(f64);
struct Feet(f64);

impl From for Feet {
    fn from(m: Meters) -> Self {
        Feet(m.0 * 3.281)
    }
}

let m = Meters(1.0);
let f = Feet::from(m);
let f: Feet = Meter(1.0).into(); // 使用Into 自动推断

// 标准库常见转换
let s: String = "hello".to_string();
let s: String = String::from("hello");
let n: i64 = 42i32.into();

字符串操作

字符串需要先区分几个概念:

类型 存在哪里 可变 本质
&str 栈(引用) 字符串切片,胖指针
String 拥抱所有权的字符串
&String 栈(引用) String的引用,尽量用&str
// - &str -> String
let s: String = "hello".to_string();
let s: String = String::from("hello");
let s: String = "hello".to_owned();

// String -> &str
let owned = String::from("hello");
let slice: &str = &owned;
let slice: &str = owned.as_str();

// 函数参数:接受&str比&String更通用
fn greet(name: &str) { println!("Hi, {}", name); }
greet("Alice");  // 字面量直接传递
greet(&String::from("Bob")); // String自动deref成&str

// - 字符串操作
let mut s = String::from("Hello");

// 追加
s.push_str(", world"); // 追加 &str
s.push("!"); // 追加单字符

// 拼接
let s1 = String::from("Hello, ");
let s2 = String::from("world");
let s3 = s1 + &s2; // s1被移走,不能再用

// 格式化拼接(推荐,不消耗所有权)
let s =format!("{} {}", "Hello", "world");

// 切片(按字节,注意UTF-8边界)
let hello = &s[0..5];

// 遍历字符
for c in "你好世界".chars() {
    println!("{}", c); // 按unicode字符遍历
}

测试

介绍: rust内置测试框架,不需要额外依赖。测试函数用#[test]标注,运行cargo test执行。

// - 单元测试
fn add(a: i32, b: i32) -> i32 { a + b }

#[cfg(test)] // 只在测试时编译
mod tests {
    use super::*; // 引入外层模块所有内容
    #[test]
    fn test_add() {
        assert_eq!(add(2, 3), 5);
    }

    #[test]
    fn test_negative() {
        assert!(add(-1, 1) == 0);
        assert_ne!(add(1, 1), 3); // 不相等断言
    }
    
    #[test]
    #[should_panic] // 预期此处会有panic
    fn test_panic() {
        panic!("崩溃");
    }
}

// - 异步测试 & 集成测试
#[tokio::test]
async fn test_async_handler() {
    let result = my_async_fn().await;
    assert_eq!(result, "excepted");
}

use <pak_name>::app::router::create_router;
use axum::http::StatusCode;

#[tokio::test]
async fn test_health_route() {
    let app = create_router().await;
    // 用axum::test或reqwest发送请求
}

// - 常用断言
assert!(expr) // expr为true
assert_eq!(left, right) // 相等
assert_ne!(left, right) // 不相等

// 带自定义错误信息
assert_eq!(a, b, "a 应该等于 b,但a={}, b={}", a, b);

// cargo test
// cargo test 运行所有测试
// cargo test  test_add 只运行名称包含test_add的
// cargo test -- --nocapture 显示println!输出

错误处理

介绍:生产环境常用thiserror定义错误,用anyhow快速传播错误

use thiserror::Error;

#[derive(Error, Debug)]
pub enum AppError {
    #[error("用户不存在: {id}")]
    UserNotFound { id: u32 },

    #[error("数据库错误")]
    Database(#[from] sqlx::Error), // from: 自动转换

    #[error("认证失败: {0}")]
    Unauthorized(String),

    #[error("参数错误: {0}")]
    BadRequest(String),
}

async fn get_user(id:u32) -> Result {
    let user = db.find(id).await?; // sql::Error自动转AppError
    Ok(user)
}

use axum::response::[IntoResponse, Response];
use axum::http:StatusCode;

impl IntoResponse for AppError {
    fn into_response(self) -> Response {
        let (statue, msg) = match &self {
            AppError::UserNotFound { .. } => (StatusCode::NOT_FOUND, self.to_string()),
            AppError::Unauthorized(_) => (StatusCode::UNAUTHORIZED, self.to_string()),
            AppError::BadRequest(_) => (StatusCode::BAD_REQUEST, self.to_string()),
            AppError::Database(_) => (StatusCode::INTERNAL_SERVER_ERROR, "内部错误".into()),
        };
        (status, Json(json!({ "error": msg }))).into_response()
    }
}

async fn get_user_handler(Path(id): Path) -> Result, AppError> {
    let user = get_user(id).await?;
    Ok(Json(user))
}

// - anyhow 快速原型/脚本场景
use anyhow::{Result, Context, bail, ensure};

// anyhow::Result可以接受任何错误类型
async fn run() -> Result<()> {
    let content = std::fs::read_to_string("config.json")
        .content("读取配置文件失败")?;
    let value: i32 = content.trim().parser()
        .context("配置不是有效数字")?;
    ensure!(value > 0, "配置值必须为正值");
    bail!("直接返回一个错误");
    
    Ok(())
}

并发

介绍:Rust的并发分两种: tokio::spawn生成异步任务(协程级),std::thread::spawn生成系统线程.Channel时任务见通信的标准方式。

tokio Channel类型

mpsc 多生产者单消费者,最常用,用于任务把结果送回主流程
oneshot 一次性,发一条消息,常用于请求-响应模式
broadcast 一发多收,适合实践广播(如WebSocket推送)
watch 只关心最新值,适合配置热更新、状态同步
// - mpsc 多任务汇聚结果
use tokio::sync::mpsc;

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

for i in 0..5 {
    let tx = tx.clone(); // 克隆发送端
    tokio::spawn(async move {
        tx.send(format!("task {} done", i)).await.unwrap();
    });
}
drop(tx);

while let Some(msg) = rx.recv().await {
    println!("{}", msg);
}

// - watch axum中的优雅关闭
use tokio::sync::watch;

let (shutdown_tx, shutdown_rx) = watch::channel(false);

let mut rx = shutdown_rx.clone();
tokio::spawn(async move {
    rx.changed().await.ok();
    println("收到关闭信号,清理资源...");
});

tokio::signal::ctrl_c().await.unwrap();
shutdown_tx.send(true).unwrap();

Future trait本质

介绍:async fn 返回的不是值,而是一个Future。Future是一个承诺,只有被runtime(tikio)驱动时才真正执行。

// 标准库定义(简化)
trait Future {
    type Output;
    fn pool(&mut self, cx: &mut Context) -> Pool;
}

enum Pool {
    Ready(T), // 完成了,返回值
    Pending, // 还没好,等会儿再问
}


// async fn 是语法糖,编译器自动生成Future实现
async fn fetch_data() -> String {
    "data".to_string()
}

// 顺序执行(慢)
let a = fetch_a().await;
let b = fetch_b().await;

// 并发执行(快)
let (a, b) = tokio::join!(fetch_a(), fetch_b());

tokio::spawn(async move {
    background_job().await;
});

// 在async函数中不能直接用普通函数阻塞操作
async fn bad() {
    std::thread::sleep(Duration::from_secs(1));
}

async fn good() {
    tokio::time.sleep(Duration::from_secs(1)).await; // 让出控制权
}

tokio::task::spawn_blocking(|| {
    blocking_db_call() // 在专用线程池中跑
}).await.unwrap();

类型系统哲学

介绍: Rust的核心理念之一:把运行时错误变成编译时错误。通过类型设计,让非法状态无法被表达。

fn get_order(user_id: u32, product_id: u32) { ... }
get_order(product_id, user_id); // 传反了,编译器不报错

// Newtype包一层
struct UserId(u32);
struct ProductId(u32);

fn get_order(user_id: UserId, product_id: ProductId) { ... }
get_order(ProductId(1), UserId(2)); // 编译报错!类型不符合

// - 使用状态机编码业务状态
struct Order { id: u64, state: State }
struct Pending;
struct Paid;
struct Shipped;

impl Order {
    // 只有Pending状态才能支付
    fn pay(self) -> Order {
        Order { id: self.id, state: Paid }
    }
}

impl Order {
    // 只有Paid状态才能发货
    fn ship(self) -> Order {
        Order { id: self.id, state: Shipped }
    }
}

let order = Order { id: 1, state: Pending };
// order.ship(); // 编译报错 Pending没有ship方法
let order = order.pay().ship(); // 正确流程


// - 零成本抽象: Rust 的 trait、泛型、迭代器等高级抽象,编译后和手写C代码相同相同 -- 抽象不带来运行时开销,这是Rust的核心承诺

let sum: i32 = (0..1000).filter(|x| x % 2 == 0).map(|x| x * x).sum();

// 编译后等价于
let mut sum = 0i32;
let mut i = 0;
while i < 1000 { id i % 2 == 0 { sum += i * i; } i += 1 ; }

Unsafe & 边界

介绍:Rust的安全保证来自编译器的静态检查。unsafe块允许你做编译器无法验证的操作,责任转移给你。

let x = 5;
let raw = &x as *count i32; // 创建裸指针,safe

unsafe {
    println!("{}", *raw); // 解引用,unsafe
}

// 调用unsafe函数
unsafe fn danger() { /* 可能做危险操作 */ }
unsafe { danger(); }

unsafe允许做的5件事

  • 解引用指针
  • 调用unsafe函数
  • 实现unsafe trait
  • 访问可变静态变量
  • 访问union字段

实际项目中unsafe的原则

  • 尽量不屑unsafe,优先找有safe封装的库
  • unsafe块尽量小,只包含真正需要的语句
  • 用注释说明为什么这里unsafe是安全的
  • 用safe的API包括unsafe,不对外暴露

// - Send & Sync 并发安全的Marker Trait
unsafe trait Send {} // 可以安全的而移动到另一个线程
unsafe trait Sync {} // 可以安全的被多线程共享引用

// 编译器自动推断
// i32, String, Vec 都是 Send + Sync
// Rc 不是 Send (引用计数非原子)
// *mut T 不是Send (裸指针)

// - FFI 调用C库
extern "C" {
    fn abs(x: i32) -> i32;
}

unsafe {
    println!("{}", abs(-5));
}

#[no_mangle]
pub extern "C" fn rust_add(a: i32, b: i32) -> i32 {
    a + b
}
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐