with one click
rust-safety
写 Rust 时使用。所有权、错误处理、unsafe 的正确实践。
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Menu
写 Rust 时使用。所有权、错误处理、unsafe 的正确实践。
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Based on SOC occupation classification
做 Cocos Creator 多机型/多分辨率适配时使用。Canvas、Widget、安全区。
Cocos Creator 用 AssetBundle 做分包/远程资源时使用。加载、释放、依赖、缓存。
优化 Cocos Creator 渲染性能时使用。合批、图集、动静分离、Label。
给 Cocos Creator 原生包做热更新时使用。version manifest、增量、校验、回滚。
写 Cocos Creator 动效/动画时使用。tween、Animation、Spine、性能与清理。
做 Cocos Creator 大量条目列表时使用。虚拟列表、节点复用。
| name | rust-safety |
| description | 写 Rust 时使用。所有权、错误处理、unsafe 的正确实践。 |
| category | language |
| tags | ["rust","所有权","安全"] |
unsafe 块或评估是否真的需要 unsafe 时。clone/Rc 强行绕过时。clone/Rc 硬绕;理解生命周期规则: 遇到借用检查报错时,先理解所有权模型要表达的约束,调整数据流或函数签名;只有在语义上确实需要共享所有权时才用 Rc/Arc,不把它们当"绕过编译器的万金油";clone 有性能代价,大结构体不随意 clone。
为什么: AI 面对 borrow checker 报错时最常见的反应是加 .clone() 或把类型换成 Rc<RefCell<T>>——这能让代码编译,但往往意味着绕开了编译器在帮你捕获的真实问题:可能是数据所有权设计不合理,可能是生命周期标注缺失。用 RefCell 把静态借用检查变成运行时 panic,在 Rust 里是倒退,不是进步。
怎么做:
Arc<T>(多线程)或 Rc<T>(单线程),但要在注释里说明为何需要共享所有权。Result + ?,库代码不 unwrap/panic;用 thiserror/anyhow 分场景规则: 可能失败的操作返回 Result<T, E>,通过 ? 传播;库 crate 的公开 API 中不使用 unwrap()/expect()/panic!()(调用方无法捕获 panic);应用程序层用 anyhow 简化错误汇聚,库层用 thiserror 定义具体错误类型。
为什么: AI 写 Rust 时的典型懒惰:let val = map.get("key").unwrap(),在 happy path 下工作,一旦 key 不存在就 unwind panic,且调用方无法在类型层面知道这里会 panic。库的职责是把所有可能的失败都表达在类型里,让调用方决定怎么处理,而不是替调用方决定"遇到这种情况就崩溃"。
怎么做:
#[derive(thiserror::Error)] 定义枚举错误类型,每个变体对应一种具体失败场景。anyhow::Result<T> + ? 快速传播,context()/with_context() 添加现场信息。unwrap → 改写成 expect("此处 key 在初始化时已保证存在") 并写明原因,或用 unreachable! 配合注释。unsafe 最小化并注释不变量;能安全抽象就封装规则: unsafe 块应尽可能小,仅包含无法用安全 API 表达的操作;每个 unsafe 块旁必须有注释,说明为何此处安全(维护的不变量是什么);能把 unsafe 封装进一个安全的函数/结构体,就不要让 unsafe 泄漏到上层调用方。
为什么: AI 倾向于在遇到生命周期或类型系统挑战时直接用 unsafe 强行转换(如 std::mem::transmute),注释一句"// 应该没问题"就提交了。这类代码的危险性在于它能编译、能通过测试,但在某个边界条件下触发未定义行为,且 Miri / sanitizer 很难覆盖到所有路径。
怎么做:
unsafe 前先问:有没有 std/bytemuck/zerocopy 等安全 crate 能做这件事?unsafe → 注释格式:// SAFETY: [解释为何此处满足 XX 的不变量,即...]。miri(cargo miri test)定期跑,检测未定义行为;CI 里加 cargo test --sanitize=address。规则: 把业务约束编码进类型系统:用枚举而非字符串/整数常量表示状态;用 newtype 模式区分语义不同的同类型值;避免用 bool 参数区分行为——该拆成两个函数。
为什么: AI 常写出 fn send_email(address: String, verified: bool),然后所有调用方都要记住"第二个参数是是否验证过",极容易传反。更好的设计是 fn send_email(address: VerifiedEmail)——传入未验证的 String 在编译时就报错,根本不存在运行时检查的必要。这是 Rust 类型系统最强大的能力,AI 却经常忽视。
怎么做:
impl 方法,非法状态转换不提供方法(类型状态机模式)。u64)→ struct UserId(u64); struct PostId(u64);,杜绝混用。Option/迭代器组合子;遵循 clippy 建议规则: 处理 Option/Result 优先使用 map、and_then、unwrap_or_else、ok_or 等组合子,而非嵌套 match;所有代码通过 cargo clippy -- -D warnings(警告即错误);cargo fmt 格式化。
为什么: AI 写 Option 处理时常退化成多层嵌套 match:match x { Some(v) => match v.get(...) { Some(r) => ..., None => ... }, None => ... }。这与 Rust 的迭代器哲学背道而驰,且可读性极差。clippy 中有数百条来自社区经验的建议,很多直接指出安全隐患(如使用了 ptr::null() 而非 ptr::null_mut()),忽视 clippy 等于放弃了免费的代码审查。
怎么做:
Option<T> 链式变换 → opt.map(|v| ...).unwrap_or_default()。.filter().map().collect()),避免显式 for + push。cargo clippy -- -D warnings && cargo fmt --check,有报警就失败。clone 绕过借用 + 库代码 unwrap// 反例 — clone 掩盖设计问题,库函数直接 unwrap
use std::collections::HashMap;
pub fn get_display_name(users: &HashMap<u64, String>, id: u64) -> String {
let users_clone = users.clone(); // ❌ 为了"方便"克隆整个 map
users_clone.get(&id).unwrap().clone() // ❌ 库代码 unwrap,调用方无法处理缺失
}
// 正例 — 零克隆借用,错误用 Result 表达
use std::collections::HashMap;
#[derive(Debug, thiserror::Error)]
pub enum UserError {
#[error("用户 {0} 不存在")]
NotFound(u64),
}
pub fn get_display_name(
users: &HashMap<u64, String>,
id: u64,
) -> Result<&str, UserError> {
users
.get(&id)
.map(String::as_str)
.ok_or(UserError::NotFound(id)) // ✅ 调用方决定如何处理缺失
}
bool 参数 + 裸字符串状态// 反例 — bool 参数语义模糊,字符串状态无法穷举
fn process_order(order_id: u64, is_priority: bool, status: &str) {
if status == "pendign" { ... } // ❌ 拼写错误,编译器不报
}
process_order(42, true, "pending"); // ❌ true 是什么意思?
// 正例 — 枚举穷举状态,newtype 明确语义
#[derive(Debug)]
enum OrderStatus { Pending, Processing, Shipped, Cancelled }
struct OrderId(u64); // ✅ newtype,不会和其他 u64 混淆
fn process_order(id: OrderId, priority: Priority, status: OrderStatus) {
match status {
OrderStatus::Pending => { ... }
OrderStatus::Processing => { ... }
// 编译器强制穷举 ✅
}
}
.clone() 或 Rc?unwrap()/expect() 调用?(应该没有)unsafe 块旁是否有 // SAFETY: 注释,说明维护的不变量?Option/Result 处理是否用组合子,而非多层嵌套 match?cargo clippy -- -D warnings 和 cargo fmt --check 全部通过?unsafe 代码是否已用 cargo miri test 验证过?