【硬核干货】Rust 基础入门全解:从所有权到并发编程一篇吃透
【硬核干货】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
}
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)