一键导入
rust-safety
写 Rust 时使用。所有权、错误处理、unsafe 的正确实践。
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
写 Rust 时使用。所有权、错误处理、unsafe 的正确实践。
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
做 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 验证过?