本文档聚焦 Rust 面试高频考点、资深开发必备知识,每道题都附带深度讲解。
Rust 的所有权系统是其内存安全保证的核心,基于三条规则:
| 方案 | 优点 | 缺点 |
|---|---|---|
| C/C++ 手动管理 | 完全控制 | 内存泄漏、悬挂指针、double free |
| Java/Go GC | 自动管理 | STW 暂停、运行时开销 |
| Rust 所有权 | 零成本抽象、编译期保证 | 学习曲线陡峭 |
fn ownership_demo() {
let s1 = String::from("hello"); // s1 拥有这块堆内存
let s2 = s1; // 所有权 move 到 s2,s1 失效
// println!("{}", s1); // 编译错误!防止 double free
println!("{}", s2); // 正确
let s3 = s2.clone(); // 深拷贝,两份独立内存
println!("s2={}, s3={}", s2, s3); // 都有效
}
面试追问:为什么移动后不能继续使用原变量?
答:如果允许多个变量指向同一块堆内存,离开作用域时会导致 double free。Rust 在编译期通过"移动语义"确保只有一个所有者,从根本上消除这类问题。
借用检查器在编译期执行以下检查:
fn borrow_rules() {
let mut data = vec![1, 2, 3];
// 规则 1:不可变借用可以有多个
let ref1 = &data;
let ref2 = &data;
println!("{} {}", ref1[0], ref2[0]); // OK
// 规则 2:可变借用只能有一个
let ref3 = &mut data;
ref3.push(4);
// println!("{}", ref1[0]); // 错误!ref1 是 data 的借用,而此时 data 被 ref3 可变借用
println!("{:?}", ref3); // OK
// 规则 3:借用生命周期不能超过所有者
// 作用域结束后才能重新借用
let ref4 = &data;
println!("{:?}", ref4);
}
核心概念:NLL(Non-Lexical Lifetimes)
Rust 2018 引入,允许借用在其最后一次使用后结束,而非作用域结束:
fn nll_demo() {
let mut s = String::from("hello");
let r = &s; // 不可变借用开始
println!("{}", r); // 最后一次使用 r,借用在此结束
let r2 = &mut s; // 可以创建新的可变借用
r2.push_str(" world");
}
三条自动推断规则:
// 规则 1:每个引用参数都有自己的生命周期
fn foo<'a, 'b>(x: &'a i32, y: &'b i32)
// 规则 2:只有一个输入生命周期,赋给所有输出
fn foo<'a>(x: &'a i32) -> &'a i32
// 规则 3:多个输入生命周期但有 &self/&mut self,self 的生命周期赋给输出
impl<'a> MyStruct<'a> {
fn method(&self) -> &T // 返回值生命周期与 self 相同
}
必须手动标注的场景:
// 场景 1:多个输入引用,输出引用可能被返回
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
// 场景 2:结构体包含引用
struct Parser<'a> {
source: &'a str,
current_pos: usize,
}
// 场景 3:返回引用但不来自 self(如函数返回引用)
fn first_word<'a>(s: &'a str) -> &'a str {
s.split_whitespace().next().unwrap_or("")
}
面试追问:longest 函数中 'a 的实际含义是什么?
答:'a 表示返回值的生命周期是两个输入生命周期中较短的那一个。这确保返回的引用在使用时,两个输入引用都仍然有效。
// ===== Trait 定义与实现 =====
trait Draw {
fn draw(&self);
}
struct Circle { radius: f64 }
struct Rectangle { width: f64, height: f64 }
impl Draw for Circle {
fn draw(&self) { println!("画圆形: r={}", self.radius); }
}
impl Draw for Rectangle {
fn draw(&self) { println!("画矩形: {}x{}", self.width, self.height); }
}
// ===== 静态分发(单态化)=====
// 编译时展开,为每个具体类型生成独立代码
fn render_static<T: Draw>(items: &[T]) {
for item in items { item.draw(); }
}
// ==================================
// 注意:只能处理单一类型,Circle 和 Rectangle 混合会失败
// ==================================
// ===== 动态分发(dyn Trait)=====
// 运行时通过 vtable 分发,有运行时开销但更灵活
fn render_dynamic(items: &[&dyn Draw]) {
for item in items { item.draw(); }
}
fn main() {
// 静态分发示例
let circles = vec![Circle { radius: 1.0 }, Circle { radius: 2.0 }];
render_static(&circles);
// 动态分发示例:可混合不同类型
let c = Circle { radius: 1.0 };
let r = Rectangle { width: 2.0, height: 3.0 };
let items: Vec<&dyn Draw> = vec![&c, &r];
render_dynamic(&items);
}
关键区别:
impl Trait / <T: Trait>:零成本抽象,编译时确定类型,代码膨胀dyn Trait:运行时动态分发,需要 &dyn 或 Box<dyn>,有 vtable 开销// === 关联类型:当 trait 对每种实现只有一个对应类型时 ===
pub trait Iterator {
type Item; // 关联类型:每种实现只有一个 Item
fn next(&mut self) -> Option<Self::Item>;
}
// 实现 Iterator 时,Item 是确定的
impl Iterator for Counter {
type Item = u32; // 只实现一次
fn next(&mut self) -> Option<Self::Item> { /* ... */ }
}
// === 泛型参数:当 trait 可多次实现不同类型时 ===
pub trait From<T> {
fn from(T) -> Self;
}
// 可以为不同类型实现 From
impl From<String> for MyType { /* ... */ }
impl From<i32> for MyType { /* ... */ }
// === 对比总结 ===
// 1. 关联类型:更简洁,调用时不需要标注类型
// 2. 泛型参数:更灵活,允许同一类型多次实现 trait
// 3. 经验法则:如果 trait 有"主要"的输出类型,用关联类型
Rc vs Arc,Box vs Rc?use std::rc::Rc;
use std::sync::Arc;
use std::cell::RefCell;
use std::sync::Mutex;
// === Box<T>:独占所有权,单线程堆分配 ===
// 场景:编译时大小未知类型、大数据转移所有权、trait 对象
fn box_demo() {
let b = Box::new([0u8; 1024]); // 防止栈溢出
process(b); // 转移所有权,高效
}
fn process(data: Box<[u8]>) { /* ... */ }
// === Rc<T>:引用计数,单线程共享所有权 ===
// 场景:图结构、多个所有者、单线程环境
fn rc_demo() {
let data = Rc::new(vec![1, 2, 3]);
let data2 = Rc::clone(&data); // 只增加引用计数
println!("引用计数: {}", Rc::strong_count(&data));
}
// === Arc<T>:原子引用计数,线程安全的 Rc ===
// 场景:多线程共享不可变数据
fn arc_demo() {
let data = Arc::new(vec![1, 2, 3]);
let data2 = Arc::clone(&data);
std::thread::spawn(move || {
println!("子线程访问: {:?}", data2);
});
}
// === RefCell<T>:运行时借用检查,单线程内部可变性 ===
fn refcell_demo() {
let data = RefCell::new(vec![1, 2, 3]);
data.borrow_mut().push(4); // 运行时检查
}
// === Mutex<T>:线程安全的内部可变性 ===
fn mutex_demo() {
let data = Mutex::new(0);
*data.lock().unwrap() += 1;
}
选型矩阵:
| 类型 | 所有权 | 线程安全 | 借用检查 | 适用场景 |
|---|---|---|---|---|
Box<T> | 独占 | 无要求 | 编译期 | 堆分配、转移所有权 |
Rc<T> | 共享 | 单线程 | 编译期 | 单线程多所有者 |
Arc<T> | 共享 | 多线程 | 编译期 | 多线程不可变共享 |
RefCell<T> | 单所有者+内部可变 | 单线程 | 运行时 | 需要修改不可变绑定 |
Rc<RefCell<T>> | 共享+内部可变 | 单线程 | 运行时 | GUI 节点等 |
Arc<Mutex<T>> | 共享+内部可变 | 多线程 | 运行时锁 | 多线程可变共享 |
内部可变性是指:即使外部绑定不可变,也能修改内部数据的模式。
use std::cell::Cell;
use std::cell::RefCell;
// === Cell<T>:T: Copy 时使用,通过 replace 实现内部可变 ===
fn cell_demo() {
let cell = Cell::new(5);
cell.set(10); // 无需 &mut self
println!("{}", cell.get()); // 10
}
// === RefCell<T>:运行时借用检查,允许非 Copy 类型 ===
struct MockMessenger {
sent_messages: RefCell<Vec<String>>,
}
impl MockMessenger {
fn send(&self, msg: &str) {
// 不可变引用 &self 下修改消息
self.sent_messages.borrow_mut().push(msg.to_string());
}
}
// === 对比:无内部可变性时无法实现 ===
// fn send_broken(&self, msg: &str) {
// self.sent_messages.push(msg.to_string()); // 错误!self 不可变
// }
面试追问:Cell 和 RefCell 的区别?
Cell<T>:适用于 Copy 类型,通过 get/set/replace 操作,无运行时借用开销RefCell<T>:适用于任意类型,通过 borrow/borrow_mut 运行时借用检查,违反规则会 panicRust 通过 Send 和 Sync trait 在编译期保证线程安全:
// === Send:所有权可以跨线程传递 ===
// 几乎所有类型都是 Send,除了 Rc、裸指针等
// === Sync:引用可以跨线程共享(&T 是 Send)===
// 如果 &T 是 Send,则 T 是 Sync
// 几乎所有不可变类型都是 Sync
// === 示例:Rc 不是 Send ===
use std::rc::Rc;
// fn test_send_not() {
// let rc = Rc::new(5);
// std::thread::spawn(move || {
// println!("{}", rc); // 编译错误!Rc 不是 Send
// });
// }
// === 正确方式:使用 Arc ===
fn test_send_ok() {
let arc = Arc::new(5);
let arc_clone = Arc::clone(&arc);
std::thread::spawn(move || {
println!("{}", arc_clone); // OK
});
println!("{}", arc);
}
// === RefCell 不是 Sync,但 Arc<RefCell<T>> 是 Send ===
fn refcell_sync() {
// Arc<Mutex<T>> 既是 Send 又是 Sync
use std::sync::{Arc, Mutex};
let data = Arc::new(Mutex::new(0));
let data2 = Arc::clone(&data);
std::thread::spawn(move || {
*data2.lock().unwrap() += 1;
});
}
核心原则:
where T: Sync) 或消息传递) 模式)<hr>
<h2>问题 9:Rust 的并发原语对比(channel vs Mutex)</h2>
参考答案:use std::sync::mpsc;
use std::sync::{Arc, Mutex};
use std::thread;
// === Channel:消息传递并发(推荐)===
// "不要通过共享内存来通信,而要通过通信来共享内存"
fn channel_demo() {
let (tx, rx) = mpsc::channel();
// 多个生产者
for i in 0..3 {
let tx = tx.clone();
thread::spawn(move || {
tx.send(format!("线程 {} 的消息", i)).unwrap();
});
}
drop(tx); // 关闭原始发送端
for msg in rx {
println!("收到: {}", msg);
}
}
// === Mutex + Arc:共享状态并发 ===
fn mutex_demo() {
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..10 {
let counter = Arc::clone(&counter);
handles.push(thread::spawn(move || {
let mut num = counter.lock().unwrap();
*num += 1;
}));
}
for h in handles { h.join().unwrap(); }
println!("结果: {}", *counter.lock().unwrap());
}
// === 异步 Channel(tokio)===
#[tokio::main]
async fn async_channel_demo() {
use tokio::sync::mpsc;
let (tx, mut rx) = mpsc::channel(32);
tokio::spawn(async move {
tx.send("hello").await.unwrap();
});
if let Some(msg) = rx.recv().await {
println!("异步收到: {}", msg);
}
}
面试追问:何时用 channel,何时用 Mutex?
<hr>
<h2>问题 10:自引用结构(Self-referential Struct)如何处理?</h2>
参考答案:自引用结构是 Rust 的难点,因为移动会导致内部指针失效:
// === 错误示例 ===
struct SelfRef {
data: String,
pointer_to_data: *const String, // 自引用指针
}
impl SelfRef {
fn new(data: String) -> Self {
let mut s = Self {
data,
pointer_to_data: std::ptr::null(),
};
s.pointer_to_data = &s.data as *const String; // 移动后指针失效!
s
}
}
// === 解决方案 1:使用 Pin + Unsafe(库作者用)===
use std::pin::Pin;
use std::marker::PhantomPinned;
struct SafeSelfRef {
data: String,
pointer_to_data: *const String,
_pin: PhantomPinned, // 禁止 Unpin
}
impl SafeSelfRef {
fn new(data: String) -> Pin<Box<Self>> {
let mut boxed = Box::pin(Self {
data,
pointer_to_data: std::ptr::null(),
_pin: PhantomPinned,
});
let ptr = &boxed.data as *const String;
unsafe {
boxed.as_mut().get_unchecked_mut().pointer_to_data = ptr;
}
boxed
}
}
// === 解决方案 2:使用索引/ID替代指针 ===
struct Node {
id: usize,
parent_id: Option<usize>, // 用索引替代指针
data: String,
}
// === 解决方案 3:使用 ouroboros/self_cell 等库 ===
<hr>
<h2>第六部分:Async/Await(现代 Rust 必备)</h2>
<h2>问题 11:Rust 的异步模型是什么?Future 原理?</h2>
参考答案:Rust 使用 无栈协程(stackless coroutines) 和 惰性 Future:
// === Future trait(简化版)===
pub trait Future {
type Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>;
}
pub enum Poll<T> {
Ready(T),
Pending,
}
// === async/await 本质是语法糖 ===
async fn example() -> i32 {
let x = async_op().await;
x + 1
}
// 编译器展开后大致等价于:
/*
impl Future for ExampleFuture {
type Output = i32;
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<i32> {
// 状态机跳转逻辑...
}
}
*/
// === Executor 和 Reactor 模型 ===
// Executor:执行 Future,调用 poll
// Reactor:I/O 事件就绪时唤醒任务
#[tokio::main]
async fn main() {
// tokio 提供 Executor + Reactor(基于 epoll/kqueue/IOCP)
let data = tokio::fs::read_to_string("Cargo.toml").await.unwrap();
}
关键概念:
让出控制权(不像抢占式线程) 约束:spawn 要求的 Future 必须是 Send(移至线程池执行):大多数 Future 是 Unpin的,但自引用 Future 不是<hr>
<h2>问题 12:Pin 和 Unpin 是什么?为什么需要?</h2>
// === 问题场景:自引用 Future ===
// 如果被 Pin 的 Future 被移动,内部指针会失效!
use std::pin::Pin;
use std::future::Future;
// === Unpin:可以安全移动(大多数类型自动实现)===
// i32, String, Vec<T> 等都是 Unpin
// === !Unpin:不能安全移动(如包含自引用的 Future)===
// async 块自动生成的 Future 通常不是 Unpin
// === Pin<Box<T>>:在堆上固定位置 ===
fn pin_demo() {
let future = async { 42 };
let pinned = Box::pin(future); // Pin<Box<Future>>
// pinned 现在在堆上固定地址,不会被移动
}
// === Pin<&mut T>:在栈上固定(不安全)===
unsafe fn pin_ref_demo() {
let mut value = async { 42 };
let pinned: Pin<&mut _> = Pin::new(&mut value);
// 只要 pinned 存在,value 就不能被移动
}
// === 实际使用:手动实现 Future 时 ===
struct MyFuture {
state: u32,
// 可能有自引用字段...
}
impl Future for MyFuture {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
let this = unsafe { self.get_unchecked_mut() };
// 通过 this 访问字段,它们地址稳定
this.state += 1;
if this.state > 3 { Poll::Ready(()) } else { Poll::Pending }
}
}
面试追问:Tokio 中 spawn 要求的 Send 是什么约束?
答:spawn 将 Future 移动到线程池中执行,可能在任意线程上 poll,所以 Future 必须是 Send(所有权可跨线程传递)。使用 LocalSet 可以在当前线程执行非 Send 的 Future。
<hr>
<h2>第七部分:Unsafe Rust</h2>
<h2>问题 13:什么情况下需要使用 unsafe?</h2>
参考答案:// === 五种 unsafe 操作 ===
// 1. 解引用裸指针
// 2. 调用 unsafe 函数/方法
// 3. 访问或修改可变静态变量
// 4. 实现 unsafe trait
// 5. 访问 union 字段
// === 场景 1:性能优化(避免边界检查)===
fn unchecked_access(slice: &[u8], index: usize) -> u8 {
unsafe {
// 假设 index 一定在范围内
*slice.get_unchecked(index)
}
}
// === 场景 2:调用外部 C 库 ===
extern "C" {
fn abs(input: i32) -> i32;
}
fn c_abs(n: i32) -> i32 {
unsafe { abs(n) }
}
// === 场景 3:底层系统编程 ===
fn raw_memory_ops() {
let ptr = Box::into_raw(Box::new(42));
unsafe {
// 手动内存管理
drop(Box::from_raw(ptr));
}
}
// === 场景 4:自引用结构(如前所述)===
// === 场景 5:实现底层数据结构 ===
struct UnsafeQueue<T> {
head: *mut Node<T>,
tail: *mut Node<T>,
}
struct Node<T> {
data: T,
next: *mut Node<T>,
}
unsafe 准则:
等工具检测未定义行为<hr>
<h2>问题 14:未定义行为(Undefined Behavior)有哪些?</h2>
参考答案:// Rust 中常见的 UB(unsafe 代码中):
// 1. 解引用空指针或悬挂指针
let p: *const i32 = std::ptr::null();
unsafe { let _ = *p; } // UB!
// 2. 读写未对齐的指针
let bytes = [0u8; 4];
let p = bytes.as_ptr().add(1);
unsafe { let _ = *(p as *const u32); } // UB! 未对齐访问
// 3. 违反别名规则(同一时刻两个 &mut)
let mut x = 5;
let r1 = &mut x as *mut i32;
let r2 = &mut x as *mut i32;
unsafe {
*r1 = 1;
*r2 = 2; // UB! 别名违规
}
// 4. 读取未初始化内存
let x: i32;
unsafe { let _ = x; } // UB! 读取未初始化
// 5. 违反生命周期/借用规则
<hr>
<h2>第八部分:内存布局与性能优化</h2>
<h2>问题 15:Rust 的枚举内存布局是如何优化的?</h2>
参考答案:// === 标签联合(Tagged Union)===
enum MyEnum {
A(u8), // 1 byte
B(u32), // 4 bytes
C(u64), // 8 bytes
}
// 内存大小 = 最大变体 + 标签(通常 1-8 bytes)
// 这里约为 8 + 标签 = 16 bytes(对齐)
// === Niche Optimization(小众优化)===
// Option<&T> 大小与 &T 相同!
// 因为 null 指针不可能是有效引用,所以用 null 表示 None
fn niche_optimization() {
println!("&i32: {} bytes", std::mem::size_of::<&i32>());
println!("Option<&i32>: {} bytes", std::mem::size_of::<Option<&i32>>());
// 都是 8 字节!
}
// === Option<Box<T>> 也是 Niche Option ===
fn niche_box() {
println!("Box<i32>: {} bytes", std::mem::size_of::<Box<i32>>());
println!("Option<Box<i32>>: {} bytes", std::mem::size_of::<Option<Box<i32>>>());
// 都是 8 字节!
}
// === RFC 2195: 用于自定义类型 ===
// 通过 NonNull、NonZero 等类型让编译器知道某些位模式无效
use std::num::NonZeroU32;
use std::ptr::NonNull;
enum SmartOption<T: ?Sized> {
Some(NonNull<T>), // Niche:null 表示 None
None,
}
面试追问:Box 和 &dyn Trait 的区别?:拥有所有权,堆分配,大小是两个指针(data + vtable):借用,不拥有,大小也是两个指针:动态大小类型(DST),不能直接作为局部变量<hr>
<h2>第九部分:宏系统</h2>
<h2>问题 16:宏的分类和区别?macro_rules! vs 过程宏</h2>
// === 1. 声明式宏 macro_rules! ===
// 基于模式匹配的文本替换
macro_rules! vec {
($($x:expr),* $(,)?) => {{
let mut v = Vec::new();
$(v.push($x);)*
v
}};
}
// === 2. 过程宏(三种)===
// 自定义 derive:为结构体/枚举自动实现 trait
#[derive(Debug, Clone)]
struct Point { x: i32, y: i32 }
// 属性宏:自定义属性
#[tokio::main]
fn main() {}
// 类函数宏:类似 macro_rules! 但更灵活
sql!(SELECT * FROM users WHERE id = 1)
// === 编写过程宏 ===
// 需要单独 crate,类型 proc-macro = true
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput};
#[proc_macro_derive(MyDerive)]
pub fn my_derive(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
let name = &ast.ident;
quote! {
impl #name {
fn created_by_derive() {}
}
}.into()
}
// === 对比 ===
// 编译时 语法能力 复杂度 使用场景
// macro_rules! 简单 有限 低 DSL、代码重复
// derive 自动 高 中 自动 trait 实现
// 属性宏 自定义 高 高 编译期转换
// 类函数宏 灵活 高 高 复杂代码生成
<hr>
<h2>第十部分:模式匹配与高级类型</h2>
<h2>问题 17:if let、while let、let-else` 的使用场景?
参考答案:
// === if let:只关心一种模式 ===
fn if_let_demo() {
let opt: Option<&str> = Some("hello");
// 不需要外层 match
if let Some(s) = opt {
println!("{}", s);
}
// 多个条件:用 if let chain
if let Some(s) = opt && !s.is_empty() {
println!("非空: {}", s);
}
}
// === while let:循环直到模式不匹配 ===
fn while_let_demo() {
let mut stack = vec![1, 2, 3];
while let Some(top) = stack.pop() {
println!("{}", top);
}
}
// === let-else:提前返回(Rust 1.65+)===
fn let_else_demo(input: Option<&str>) -> Result<&str, &'static str> {
let Some(value) = input else {
return Err("input is None");
};
// value 从这里开始可用,类型为 &str
Ok(value)
}
// === matches! 宏:快速判断模式 ===
fn matches_demo(x: Option<i32>) -> bool {
matches!(x, Some(1..=100 | 200))
}
// === 示例:迭代器 vs 手写循环 ===
// 看起来高层次的代码
fn sum_iterator(nums: &[i32]) -> i32 {
nums.iter().filter(|&&x| x > 0).sum()
}
// 编译器优化后等价于手写
fn sum_manual(nums: &[i32]) -> i32 {
let mut sum = 0;
for &num in nums {
if num > 0 {
sum += num;
}
}
sum
}
// 两者生成几乎相同的汇编代码(release 模式)
// 这就是"零成本":高层抽象不引入运行时开销
use std::ops::Deref;
struct MyRc<T> {
inner: *const Inner<T>,
}
struct Inner<T> {
value: T,
strong: usize,
weak: usize,
}
impl<T> MyRc<T> {
fn new(value: T) -> Self {
let inner = Box::leak(Box::new(Inner {
value,
strong: 1,
weak: 0,
}));
MyRc { inner }
}
fn strong_count(&self) -> usize {
unsafe { (*self.inner).strong }
}
}
impl<T> Clone for MyRc<T> {
fn clone(&self) -> Self {
unsafe {
(*self.inner).strong += 1;
}
MyRc { inner: self.inner }
}
}
impl<T> Deref for MyRc<T> {
type Target = T;
fn deref(&self) -> &T {
unsafe { &(*self.inner).value }
}
}
impl<T> Drop for MyRc<T> {
fn drop(&mut self) {
unsafe {
(*self.inner).strong -= 1;
if (*self.inner).strong == 0 {
// 最后一次引用释放
drop(Box::from_raw(self.inner as *mut Inner<T>));
}
}
}
}
// 安全:Send + Sync 的判断实现(简化版)
// 不完全正确,仅供演示结构
unsafe impl<T: Send + Sync> Send for MyRc<T> {}
unsafe impl<T: Send + Sync> Sync for MyRc<T> {}
struct MinStack {
stack: Vec<i32>,
min_stack: Vec<i32>, // 每个状态对应的当前最小值
}
impl MinStack {
fn new() -> Self {
MinStack {
stack: Vec::new(),
min_stack: Vec::new(),
}
}
fn push(&mut self, val: i32) {
self.stack.push(val);
let min = self.min_stack.last()
.map(|&m| m.min(val))
.unwrap_or(val);
self.min_stack.push(min);
}
fn pop(&mut self) -> Option<i32> {
self.min_stack.pop();
self.stack.pop()
}
fn top(&self) -> Option<&i32> {
self.stack.last()
}
fn get_min(&self) -> Option<&i32> {
self.min_stack.last()
}
}
// === 使用示例 ===
fn main() {
let mut stack = MinStack::new();
stack.push(-2);
stack.push(0);
stack.push(-3);
println!("最小值: {:?}", stack.get_min()); // Some(-3)
stack.pop();
println!("顶部: {:?}", stack.top()); // Some(0)
println!("最小值: {:?}", stack.get_min()); // Some(-2)
}
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use std::time::Duration;
use tokio::time::{sleep, Sleep};
struct WithTimeout<F: Future> {
future: F,
sleep: Pin<Box<Sleep>>,
}
impl<F: Future> Future for WithTimeout<F> {
type Output = Result<F::Output, ()>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
// 检查超时
if let Poll::Ready(()) = self.sleep.as_mut().poll(cx) {
return Poll::Ready(Err(()));
}
// 检查 Future 是否完成
unsafe {
// 安全:self 已 Pin,future 字段不会被移动
let this = &mut *self.get_unchecked_mut();
match Pin::new_unchecked(&mut this.future).poll(cx) {
Poll::Ready(output) => Poll::Ready(Ok(output)),
Poll::Pending => Poll::Pending,
}
}
}
}
fn with_timeout<F: Future>(duration: Duration, future: F) -> WithTimeout<F> {
WithTimeout {
future,
sleep: Box::pin(sleep(duration)),
}
}
// === 使用 ===
#[tokio::main]
async fn main() {
let result = with_timeout(Duration::from_secs(1), async {
sleep(Duration::from_millis(500)).await;
42
}).await;
assert_eq!(result, Ok(42));
}
use std::sync::{Arc, Mutex, Condvar};
use std::collections::VecDeque;
use std::time::Duration;
struct Connection;
struct ConnectionPool {
connections: Mutex<VecDeque<Connection>>,
condvar: Condvar,
max_size: usize,
}
impl ConnectionPool {
fn new(max_size: usize) -> Self {
ConnectionPool {
connections: Mutex::new(VecDeque::with_capacity(max_size)),
condvar: Condvar::new(),
max_size,
}
}
fn get(&self) -> Result<PooledConnection, ()> {
let mut guard = self.connections.lock().map_err(|_| ())?;
loop {
if let Some(conn) = guard.pop_front() {
return Ok(PooledConnection {
pool: self,
conn: Some(conn),
});
}
// 池为空,等待归还
if guard.len() < self.max_size {
// 创建新连接
return Ok(PooledConnection {
pool: self,
conn: Some(Connection),
});
}
// 等待通知
let (guard, _) = self.condvar.wait_timeout(guard, Duration::from_secs(30))
.map_err(|_| ())?;
if guard.is_empty() {
return Err(()); // 超时
}
}
}
fn release(&self, conn: Connection) {
let mut guard = self.connections.lock().unwrap();
guard.push_back(conn);
self.condvar.notify_one();
}
}
struct PooledConnection<'a> {
pool: &'a ConnectionPool,
conn: Option<Connection>,
}
impl<'a> Drop for PooledConnection<'a> {
fn drop(&mut self) {
if let Some(conn) = self.conn.take() {
self.pool.release(conn);
}
}
}
// === 1. Newtype 模式:包装类型实现外部 trait ===
use std::fmt;
struct Wrapper(Vec<String>);
impl fmt::Display for Wrapper {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "[{}]", self.0.join(", "))
}
}
// === 2. RAII 守卫模式 ===
struct Guard;
impl Drop for Guard { fn drop(&mut self) { /* 清理 */ } }
// === 3. 建造者模式 ===
struct Config {
host: String,
port: u16,
timeout: Duration,
}
impl Config {
fn builder() -> ConfigBuilder {
ConfigBuilder::default()
}
}
#[derive(Default)]
struct ConfigBuilder {
host: Option<String>,
port: Option<u16>,
timeout: Option<Duration>,
}
impl ConfigBuilder {
fn host(mut self, h: &str) -> Self { self.host = Some(h.to_string()); self }
fn port(mut self, p: u16) -> Self { self.port = Some(p); self }
fn build(self) -> Result<Config, &'static str> {
Ok(Config {
host: self.host.ok_or("missing host")?,
port: self.port.unwrap_or(8080),
timeout: self.timeout.unwrap_or(Duration::from_secs(30)),
})
}
}
// === 4. 策略模式(泛型参数 + trait)===
trait CompressionStrategy {
fn compress(&self, data: &[u8]) -> Vec<u8>;
}
struct Compressor<S: CompressionStrategy> {
strategy: S,
}
impl<S: CompressionStrategy> Compressor<S> {
fn compress(&self, data: &[u8]) -> Vec<u8> {
self.strategy.compress(data)
}
}
// === 5. 访问者模式 ===(通过 enum + match 实现)===
// Drop 顺序:与声明顺序相反(类似栈)
struct Foo;
impl Drop for Foo { fn drop(&mut self) { println!("A"); } }
struct Bar;
impl Drop for Bar { fn drop(&mut self) { println!("B"); } }
fn main() {
let _a = Foo;
let _b = Bar;
// 输出: B A
}
// 字段顺序:声明顺序的逆序
struct Container {
first: Foo,
second: Bar,
}
// 三种捕获方式,按优先级确定:
// 1. &T(不可变借用)- 如果闭包体不需要修改
// 2. &mut T(可变借用)- 如果闭包体需要修改
// 3. T(获取所有权)- 如果变量在闭包后不再使用,或使用 move 关键字
fn closure_capture() {
let mut x = 5;
// 自动判断为不可变借用
let print_x = || println!("x = {}", x);
print_x();
// 自动判断为可变借用(使用 FnMut)
let mut add_to_x = || x += 1;
add_to_x();
// move:获取所有权
let own_x = move || {
let y = x; // x 被移动进闭包
y
};
// println!("{}", x); // 错误!x 已被移动
}
// 1. 泛型函数 + trait bound
fn process<T: Draw + Display>(items: &[T]) {
for item in items {
item.draw();
println!("{}", item);
}
}
// 2. 默认方法 + trait 扩展
trait Loggable: Display {
fn log(&self) {
println!("[LOG] {}", self);
}
}
// 3. 宏展开重复结构
macro_rules! define_api {
($name:ident) => {
struct $name;
impl $name {
fn new() -> Self { $name }
fn execute(&self) { println!("Executing {}", stringify!($name)); }
}
};
}
define_api!(CreateHandler);
define_api!(DeleteHandler);
// 4. 组合优于继承
struct Logger { /* ... */ }
trait LoggerTrait { fn log(&self, msg: &str); }
struct Service<L: LoggerTrait> {
logger: L,
}
"Rust 的学习曲线在前面,收益在后面。" —— Jim Blandy
祝你面试顺利!🦀