Rust 异步驱动语言特性¶
日期:2026-06-25 标签:rust, async, generic, unsafe, smart-pointer, send, sync, deref
范围:异步串口项目用到的 Rust 语言特性 目的:下次回来能快速回忆
泛型¶
泛型 = 编译期多态。同一段代码支持多种类型,编译时为每个具体类型生成独立代码(单态化),零运行时开销。
fn largest<T: PartialOrd>(list: &[T]) -> &T { ... }
largest(&[1, 2, 3]); // 生成 largest<i32>
largest(&["a", "b"]); // 生成 largest<&str>
trait bound 是冒号后的约束,泛型代码能调用该 trait 的方法(编译期确认)。本项目示例:
| 维度 | 泛型(静态) | trait object(动态) |
|---|---|---|
| 写法 | T: Trait |
dyn Trait |
| 性能 | 零开销 | 每次调用 ~5-10ns vtable |
| 代码 | 每个类型一份 | 共享一份 |
| 灵活性 | 编译期固定 | 运行时可换 |
热路径选泛型。本项目 AsyncUartDriver<R, W, U> 用三泛型。
type alias 折叠复杂类型:
// uart_init.rs
pub type ArceOsDriver = AsyncUartDriver<ArceOsRuntime, ArceOsWakerSet, ArceOsUartPort>;
编译期纯替换,零开销。
PhantomData 保留"逻辑上有但实际无"的类型:
R 不出现在字段里(只在 spawn 时用 R::spawn),但仍是泛型参数。两作用:满足"未使用类型参数"检查;保留 Send/Sync 边界。
unsafe¶
unsafe 解锁 5 种能力:
| 能力 | 例子 |
|---|---|
| 解引用裸指针 | *const T / *mut T |
| 调用 unsafe fn | Uart16550::new_mmio(...) |
访问 static mut |
static mut X: T |
| 实现 unsafe trait | unsafe impl Send for T |
| 访问 union 字段 | union { ... } |
SAFETY 注释约定:任何 unsafe 代码块必须有 // SAFETY: 注释,说明这次 unsafe 为何安全。
// uart_init.rs:62
SpinNoIrq::new(unsafe {
// SAFETY:
// 1. get_uart_mmio_virt() 返回 axruntime 启动时建立的合法虚拟地址
// 2. UART 在 SpinNoIrq 内,不会被并发访问
// 3. stride=1 正确(NS16550 字节寻址)
Uart16550::new_mmio(...)
});
本项目的 unsafe 用法:
| 位置 | 类型 | 原因 |
|---|---|---|
uart_init.rs:62 |
unsafe 块 | new_mmio 接受 NonNull |
isr.rs:42 |
unsafe fn | 读 MMIO |
ring_buffer.rs:79 |
unsafe 块 | UnsafeCell 内部可变性 |
driver.rs:136 |
unsafe impl | 编译器无法自动证明 Send/Sync |
智能指针¶
Box<T> 堆分配,单一所有者,drop 时释放。常用:dyn Trait 持有、递归类型。
// ntty_async.rs
process_mode: ProcessMode::External(Box::new(move |waker| {
uart_init::driver().rx.poll.register(&waker);
}))
Arc<T> 原子引用计数,跨线程共享。
| 类型 | 计数 | 线程 | 性能 |
|---|---|---|---|
Box<T> |
单一所有者 | 取决于 T | 零开销 |
Rc<T> |
引用计数 | ❌ 单线程 | 极快 |
Arc<T> |
原子引用计数 | ✅ 跨线程 | clone/drop 几 ns |
UnsafeCell 内部可变性:
// ring_buffer.rs
pub struct RingBufRx<W: OsWakerSet> {
writer: UnsafeCell<Writer<'static>>,
reader: UnsafeCell<Reader<'static>>,
pub poll: W,
}
Writer / Reader 方法要 &mut self,但 RingBufRx 的 API 用 &self。UnsafeCell 告诉编译器"我接管"。安全性由调用方保证(SPSC 约定:只有一个写者、一个读者)。
Once<T> 单次初始化:
// uart_init.rs
static DRIVER: Once<Arc<ArceOsDriver>> = Once::new();
DRIVER.call_once(|| driver); // 只第一次执行
lazy_static! 内部用 Once + Deref 实现。
Send / Sync¶
| Trait | 含义 | 自动 |
|---|---|---|
Send |
T 所有权可跨线程 move | 大多数类型自动 |
Sync |
&T 可跨线程共享 |
大多数类型自动 |
T: Sync 当且仅当 &T: Send。
非 Send/Sync:
| 类型 | Send | Sync | 原因 |
|---|---|---|---|
Rc<T> |
❌ | ❌ | 引用计数非原子 |
RefCell<T> |
✅ | ❌ | 借用检查非线程安全 |
*mut T |
❌ | ❌ | 裸指针无同步 |
unsafe impl:
// driver.rs:136
unsafe impl<R, W, U> Send for AsyncUartDriver<R, W, U> {}
unsafe impl<R, W, U> Sync for AsyncUartDriver<R, W, U> {}
AsyncUartDriver 内部有 &'static U(裸引用)+ UnsafeCell,编译器无法自动证明。实际安全:U: Send + Sync(trait 约束);RingBufRx/Tx<W>: Send + Sync(unsafe impl);原子类型天然 Send + Sync。Send/Sync 是编译期检查,违反就编译失败。
引用与 Deref¶
| 类型 | 安全 | 可空 | 借用检查 |
|---|---|---|---|
&T / &mut T |
✅ | ❌ | ✅ 编译期 |
*const T / *mut T |
❌ | ✅ | ❌ |
NonNull<T> |
编译期非空 | ❌ | ❌ |
NonNull<u8> 用于构造全局 UART,绕开借用检查。
Deref trait:
让自定义类型"表现得像" &Target。
Deref coercion 自动转换:
本项目的 Deref 链——uart_instance().lock() 完整调用:
&lazy_static::UART (1)
↓ Deref
&SpinNoIrq<Uart16550> (2)
↓ .lock()
SpinNoIrqGuard<'_, Uart16550>
↓ Deref
&mut Uart16550 (3)
↓ .receive_bytes(buf)
3 次 Deref 全部自动,编译器完成。
Guard 模式:
// kspin 内部(伪代码)
impl<T> Drop for SpinNoIrqGuard<'_, T> {
fn drop(&mut self) {
// 恢复 sstatus.SIE
}
}
Guard 实现 Deref + DerefMut + Drop:Deref 让你能调 T 的方法;Drop 自动释放锁。
RefCell 没用。本项目用 SpinNoIrq 代替 RefCell:
| RefCell | SpinNoIrq | |
|---|---|---|
| 借用检查 | 运行时 | 编译期 |
| 线程安全 | ❌ | ✅ 关中断 |
| 失败时 | panic | 永不失败(spin) |
SpinNoIrq 更适合 ISR + 任务上下文的互斥。