1.
概述
❱
1.1.
为什么需要 Rust 编码规范
1.2.
编码规范基本约定
2.
代码风格
❱
2.1.
命名
❱
2.1.1.
P.NAM.01 同一个crate中标识符的命名规则应该使用统一的词序
2.1.2.
P.NAM.02 为 cargo feature 命名时不应含有无意义的占位词
2.1.3.
P.NAM.03 标识符命名应该符合阅读习惯
2.1.4.
P.NAM.04 作用域越大命名越精确,反之应简短
2.1.5.
P.NAM.05 用于访问或获取数据的 getter 类方法通常不要使用 get_ 前缀
2.1.6.
P.NAM.06 遵循 iter/ iter_mut/ into_iter 规范来生成迭代器
2.1.7.
P.NAM.07 避免使用语言内置保留字、关键字、内置类型和trait等特殊名称
2.1.8.
P.NAM.08 避免在变量的命名中添加类型标识
2.1.9.
P.NAM.09 定义全局静态变量时需加前缀G_以便和常量有所区分
2.1.10.
G.NAM.01 使用统一的命名风格
2.1.11.
G.NAM.02 类型转换函数命名需要遵循所有权语义
2.2.
格式
❱
2.2.1.
P.FMT.01 使用 rustfmt 进行自动格式化代码
2.2.2.
P.FMT.02 缩进使用空格而非制表符
2.2.3.
P.FMT.03 行间距最大宽度空一行
2.2.4.
P.FMT.04 语言项(Item) 定义时左花括号(brace)位置应该与语言项保持同一行
2.2.5.
P.FMT.05 存在多个标识符时应该保持块状(Block)缩进
2.2.6.
P.FMT.06 当有多行表达式操作时,操作符应该置于行首
2.2.7.
P.FMT.07 枚举变体和结构体字段都应左对齐
2.2.8.
P.FMT.08 函数参数超过五个或导入模块个数超过四个需换行
2.2.9.
P.FMT.09 不同的场景,使用不同的空格风格
2.2.10.
P.FMT.10 match 分支应该具有良好的可读性
2.2.11.
P.FMT.11 导入模块分组应该具有良好的可读性
2.2.12.
P.FMT.12 声明宏分支应该具有良好的可读性
2.2.13.
P.FMT.13 具名结构体字段初始化时不要省略字段名
2.2.14.
P.FMT.14 extern 外部函数需要显式指定 C-ABI
2.2.15.
P.FMT.15 解构元组的时候允许使用..来指代剩余元素
2.2.16.
P.FMT.16 不要将派生宏中多个不相关的特质合并为同一行
2.3.
注释
❱
2.3.1.
P.CMT.01 代码能做到自注释,文档要干练简洁
2.3.2.
P.CMT.02 注释应该有宽度限制
2.3.3.
P.CMT.03 使用行注释而避免使用块注释
2.3.4.
P.CMT.04 文件头注释包含版权说明
2.3.5.
P.CMT.05 在注释中使用 FIXME 和 TODO 来帮助任务协作
2.3.6.
G.CMT.01 在公开的返回Result类型的函数文档中增加 Error 注释
2.3.7.
G.CMT.02 如果公开的API在某些情况下会发生Panic,则相应文档中需增加 Panic 注释
2.3.8.
G.CMT.03 在文档注释中要使用空格代替 tab
3.
编码实践
❱
3.1.
常量
❱
3.1.1.
G.CNS.01 对于科学计算中涉及浮点数近似值的常量宜使用预定义常量
3.1.2.
G.CNS.02 不应断言常量布尔类型
3.1.3.
G.CNS.03 不应将内部可变性容器声明为常量
3.1.4.
G.CNS.04 不应在常量定义中增加显式的 'static 生命周期
3.1.5.
G.CNS.05 对于适用 const fn 的函数或方法宜尽可能地使用 const fn
3.2.
静态变量
❱
3.2.1.
G.STV.01 不宜直接使用可变静态变量作为全局变量
3.3.
本地变量
❱
3.3.1.
P.VAR.01 一般情况下避免先声明可变变量再赋值
3.3.2.
P.VAR.02 利用变量遮蔽功能保证变量安全使用
3.3.3.
G.VAR.01 以解构元组方式定义超过四个变量时不应使用太多无意义变量名
3.3.4.
G.VAR.02 不应使用非 ASCII 字符作为标识符
3.3.5.
G.VAR.03 变量遮蔽功能应当合理使用
3.3.6.
G.VAR.04 避免因局部变量过大而导致的大量栈分配
3.4.
数据类型
❱
3.4.1.
P.TYP.01 必要时,应使类型可以表达更明确的语义,而不是只是直接使用原生类型
3.4.2.
G.TYP.01 类型转换尽可能使用安全的转换函数代替 as
3.4.3.
G.TYP.02 数字字面量在使用的时候应该明确标注类型
3.4.4.
G.TYP.03 不要用数字类型边界值判断能否安全转换,而应使用 try_from 方法
3.4.5.
布尔
❱
3.4.5.1.
G.TYP.BOL.01 不应将布尔值和布尔字面量进行比较
3.4.5.2.
G.TYP.BOL.02 如果 match 匹配表达式为布尔类型,宜使用 if 表达式来代替
3.4.5.3.
G.TYP.BOL.03 不应将数字类型转换为布尔值
3.4.5.4.
G.TYP.BOL.04 禁止在if表达式条件中使用块结构
3.4.5.5.
G.TYP.BOL.05 非必要时,布尔运算应使用逻辑运算符( &&/||)而非位运算符 (&/|)
3.4.5.6.
G.TYP.BOL.06 不应使用数字代替布尔值
3.4.5.7.
G.TYP.BOL.07 使用 .not() 方法代替逻辑取反运算符 (!)
3.4.6.
字符
❱
3.4.6.1.
G.TYP.CHR.01 不应将字符字面量强制转换为 u8
3.4.6.2.
G.TYP.CHR.02 字符串方法中如果需要单个字符的值作为参数,宜使用字符而非字符串
3.4.6.3.
G.TYP.CHR.03 需要将整数转换为字符时,应使用安全转换函数,而非 transmute
3.4.7.
整数
❱
3.4.7.1.
G.TYP.INT.01 在用整数计算的时候需要考虑整数溢出、回绕和截断的风险
3.4.7.2.
G.TYP.INT.02 避免在有符号整数和无符号整数之间进行强制转换
3.4.7.3.
G.TYP.INT.03 对负数取模计算的时候不应使用 %
3.4.8.
浮点数
❱
3.4.8.1.
G.TYP.FLT.01 使用浮点数字面量时,要警惕是否存在被Rust编译器截断的风险
3.4.8.2.
G.TYP.FLT.02 从任何数字类型转换为浮点类型时注意避免损失精度
3.4.8.3.
G.TYP.FLT.03 对精度高要求的场景下,使用浮点数进行运算和比较时需要注意精度损失
3.4.8.4.
G.TYP.FLT.04 宜使用Rust内置方法处理浮点数计算
3.4.8.5.
G.TYP.FLT.05 禁止在浮点数和整数相互转换时使用 transmute
3.4.9.
切片
❱
3.4.9.1.
P.TYP.SLC.01 宜使用切片迭代器来代替手工索引
3.4.9.2.
P.TYP.SLC.02 宜使用切片模式来提升代码的可读性
3.4.10.
元组
❱
3.4.10.1.
G.TYP.TUP.01 使用元组时,其元素不宜超过3个
3.4.11.
固定长度数组
❱
3.4.11.1.
G.TYP.ARR.01 创建大全局数组时宜使用静态变量而非常量
3.4.11.2.
G.TYP.ARR.02 使用数组索引时禁止越界访问
3.4.11.3.
G.TYP.ARR.03 当数组元素为原生数据类型(Primitive),排序时优先选用非稳定排序
3.4.12.
动态数组
❱
3.4.12.1.
P.TYP.VEC.01 非必要时不宜使用动态数组
3.4.12.2.
P.TYP.VEC.02 创建动态数组时,宜预先分配足够容量,避免后续操作中产生多次分配
3.4.12.3.
G.TYP.VEC.01 禁止访问未初始化的数组
3.4.13.
结构体
❱
3.4.13.1.
P.TYP.SCT.01 为结构体实现构造性方法时,避免构造后再初始化的情况
3.4.13.2.
P.TYP.SCT.02 结构体实例需要默认实现时,宜使用Default特质
3.4.13.3.
G.TYP.SCT.01 对外导出的公开的 Struct,宜添加#[non_exhaustive]属性
3.4.13.4.
G.TYP.SCT.02 当结构体中有超过三个布尔类型的字段,宜将其独立为新的枚举类
3.4.13.5.
G.TYP.SCT.03 宜使用结构体功能更新语法来提升代码可读性
3.4.14.
枚举体
❱
3.4.14.1.
G.TYP.ENM.01 合理使用map和and_then方法
3.4.14.2.
G.TYP.ENM.02 不应自行创建空枚举
3.4.14.3.
G.TYP.ENM.03 在使用类似 C 语言的枚举写法且使用repr(isize/usize) 布局时注意 32位架构上截断的问题
3.4.14.4.
G.TYP.ENM.04 不宜在use语句中引入Enum的全部变体(variants)
3.4.14.5.
G.TYP.ENM.05 对外导出的公开Enum,宜添加#[non_exhaustive]属性
3.4.14.6.
G.TYP.ENM.06 Enum内变体的大小差异不宜过大
3.4.14.7.
G.TYP.ENM.07 如需依赖 Enum 中变体的序数,则应显示为变体设置明确的数值
3.5.
表达式
❱
3.5.1.
G.EXP.01 当需要对表达式求值后重新赋值时,宜使用复合赋值模式
3.5.2.
G.EXP.02 不宜在比较中使用不兼容的位掩码
3.5.3.
G.EXP.03 不应利用数组表达式的边界检查来 Panic,而应使用断言
3.5.4.
G.EXP.04 自增或自减运算使用+=或-=
3.5.5.
G.EXP.05 使用括号来清楚表示表达式的计算顺序
3.5.6.
G.EXP.06 避免在比较中添加无用的掩码操作
3.6.
控制流程
❱
3.6.1.
P.CTF.01 避免滥用迭代器
3.6.2.
P.CTF.02 优先使用模式匹配而非判断后再取值
3.6.3.
G.CTF.01 当需要通过多个if判断来比较大小来区分不同情况时,优先使用match和cmp来代替if表达式
3.6.4.
G.CTF.02 if条件表达式分支中如果包含了else if分支也应该包含else分支
3.6.5.
G.CTF.03 如果要通过 if 条件表达式来判断是否 Panic,请优先使用断言
3.6.6.
G.CTF.04 在 Match 分支的 Guard 语句中不要使用带有副作用的条件表达式
3.7.
字符串
❱
3.7.1.
P.STR.01 处理字符串元素时优先按字节处理而非字符
3.7.2.
P.STR.02 创建字符串时,宜预先分配大约足够的容量来避免后续操作中产生多次分配
3.7.3.
P.STR.03 在使用内建字符串处理函数或方法的时候,应注意避免隐藏的嵌套迭代或多次迭代
3.7.4.
P.STR.04 在使用 Cow<'a, B> 时要注意选择合理场景以便最大化地优化性能
3.7.5.
P.STR.05 在拼接字符串时,优先使用format!
3.7.6.
G.STR.01 在实现Display特质时不应调用to_string()方法
3.7.7.
G.STR.02 在追加字符串时使用push_str方法
3.7.8.
G.STR.03 将只包含 ASCII字符的字符串字面量转为字节序列可以直接使用b"str" 语法代替调用as_bytes方法
3.7.9.
G.STR.04 需要辨别字符串的字符开头或结尾字符时,不应按字符迭代比较
3.7.10.
G.STR.05 对字符串按指定位置进行切片的时候需要小心破坏其 UTF-8 编码
3.8.
集合容器
❱
3.8.1.
P.CLT.01 创建HashMap、VecDeque时,可以预先分配大约足够的容量来避免后续操作中产生多次分配
3.8.2.
G.CLT.01 非必要情况下,不要使用LinkedList,而用Vec或VecDeque代替
3.9.
函数设计
❱
3.9.1.
P.FUD.01 传递到闭包的变量建议单独重新绑定
3.9.2.
P.FUD.02 函数返回值不要使用 return
3.9.3.
G.FUD.01 函数参数最长不要超过五个
3.9.4.
G.FUD.02 当函数参数实现了 Copy,并且是按值传入,如果值可能会太大,则宜考虑按引用传递
3.9.5.
G.FUD.03 当函数参数出现太多 bool 类型的参数时,应该考虑将其封装为自定义的结构体或枚举
3.9.6.
G.FUD.04 当Copy 类型的足够小的值作为函数参数时,应该按值(by-value)传入,而不是引用(by-ref)
3.9.7.
G.FUD.05 不要总是为函数指定 inline(always)
3.9.8.
G.FUD.06 函数参数应该考虑兼容多种类型
3.10.
泛型
❱
3.10.1.
P.GEN.01 用泛型来抽象公共语义
3.10.2.
P.GEN.02 不要随便使用 impl Trait 语法替代泛型限定
3.10.3.
P.GEN.03 不要使用太多泛型参数和 trait 限定,否则会增长编译时间
3.10.4.
P.GEN.04 为泛型类型实现方法时,impl 中声明的泛型类型参数一定要被用到
3.10.5.
P.GEN.05 定义泛型函数时,如果该函数实现用到来自 trait 定义的相关行为,需要为泛型指定相关 trait 的限定
3.10.6.
G.GEN.01 不要在泛型位置上使用内建类型
3.10.7.
G.GEN.02 使用 Rust 标准库中某些方法,要注意避免使用其泛型默认实现,而应该使用具体类型的实现
3.11.
特质
❱
3.11.1.
P.TRA.01 使用 trait 时要注意 trait 一致性规则
3.11.2.
标准库内置 trait
❱
3.11.2.1.
P.TRA.BLN.01 在实现Borrow特质时,需要注意一致性
3.11.2.2.
G.TRA.BLN.01 应该具体类型的 default() 方法代替 Default::default() 调用
3.11.2.3.
G.TRA.BLN.02 不要为迭代器实现Copy 特质
3.11.2.4.
G.TRA.BLN.03 能使用派生宏(Derive)自动实现Default特质就不要用手工实现
3.11.2.5.
G.TRA.BLN.04 在使用#[derive(Hash)] 的时候,避免再手工实现 PartialEq
3.11.2.6.
G.TRA.BLN.05 在使用#[derive(Ord)] 的时候,避免再手工实现 PartialOrd
3.11.2.7.
G.TRA.BLN.06 不要对实现 Copy 或引用类型调用 std::mem::drop 和 std::mem::forgot
3.11.2.8.
G.TRA.BLN.07 对实现 Copy 的可迭代类型来说,要通过迭代器拷贝其所有元素时,应该使用 copied方法,而非cloned
3.11.2.9.
G.TRA.BLN.08 实现 From 而不是 Into
3.11.2.10.
G.TRA.BLN.09 一般情况下不要给 Copy 类型手工实现 Clone
3.11.2.11.
G.TRA.BLN.10 不要随便使用Deref特质来模拟继承
3.11.3.
trait 对象
❱
3.11.3.1.
P.TRA.OBJ.01 根据场景合理选择使用trait对象或泛型静态分发
3.11.3.2.
P.TRA.OBJ.02 除非必要,避免自定义虚表
3.12.
错误处理
❱
3.12.1.
P.ERR.01 当传入函数的参数值因为超出某种限制可能会导致函数调用失败,应该使用断言
3.12.2.
P.ERR.02 在确定 Option 和 Result<T, E>类型的值不可能是 None 或 Err 时,请用 expect 代替 unwrap()
3.12.3.
G.ERR.01 在处理 Option 和 Result<T, E> 类型时,不要随便使用 unwrap
3.12.4.
G.ERR.02 不要滥用 expect,请考虑用 unwrap_or_ 系列方法代替
3.13.
内存管理
❱
3.13.1.
生命周期
❱
3.13.1.1.
P.MEM.LFT.01 生命周期参数命名尽量有意义且简洁
3.13.1.2.
P.MEM.LFT.02 通常需要显式地标注生命周期,而非利用编译器推断
3.13.2.
智能指针
❱
3.13.2.1.
P.MEM.SPT.01 使用 RefCell<T> 时宜使用 try_borrow/try_borrow_mut 方法
3.13.3.
Box 类型
❱
3.13.3.1.
G.MEM.BOX.01 一般情况下,不应直接对 Box<T> 进行借用
3.13.3.2.
G.MEM.BOX.02 一般情况下,不应直接对已经在堆上分配内存的类型进行 Box 装箱
3.13.3.3.
G.MEM.BOX.03 一般情况下,不应直接对栈分配类型进行 Box 装箱
3.13.4.
Drop 析构
❱
3.13.4.1.
G.MEM.DRP.01 要注意防范内存泄漏
3.14.
模块
❱
3.14.1.
P.MOD.01 合理控制对外接口和模块之间的可见性
3.14.2.
P.MOD.02 将模块的测试移动到单独的文件,有助于增加编译速度
3.14.3.
G.MOD.01 使用导入模块中的类型或函数,在某些情况下需要带模块名前缀
3.14.4.
G.MOD.02 如果是作为库供别人使用,在 lib.rs中重新导出对外类型、函数和 trait 等
3.14.5.
G.MOD.03 导入模块不要随便使用 通配符*
3.14.6.
G.MOD.04 一个项目中应该避免使用不同的模块布局风格
3.14.7.
G.MOD.05 不要在私有模块中设置其内部类型或函数方法为 pub(crate)
3.15.
包管理
❱
3.15.1.
P.CAR.01 应该尽量把项目划分为合理的 crate 组合
3.15.2.
P.CAR.02 不要滥用 Features
3.15.3.
P.CAR.03 使用 cargo features 来代替 --cfg 条件编译参数
3.15.4.
P.CAR.04 宜使用 cfg! 来代替 #[cfg]
3.15.5.
G.CAR.01 当项目是可执行程序而非库时,建议使用 src/main.rs 和 src/lib.rs 模式
3.15.6.
G.CAR.02 Crate 的 Cargo.toml 中应该包含必要的元信息
3.15.7.
G.CAR.03 Feature 命名应该避免否定式或多余的前后缀
3.15.8.
G.CAR.04 Cargo.toml 中依赖包版本不应使用通配符
3.16.
宏
❱
3.16.1.
P.MAC.01 不要轻易使用宏
3.16.2.
P.MAC.02 实现宏语法的时候,应该尽量贴近 Rust 语法
3.16.3.
G.MAC.01 dbg!() 宏只应该用于调试代码
3.16.4.
G.MAC.02 使用宏时应该考虑宏展开会让编译文件膨胀的影响
3.16.5.
声明宏
❱
3.16.5.1.
P.MAC.DCL.01 不要将声明宏内的变量作为外部变量使用
3.16.5.2.
P.MAC.DCL.02 在编写多个宏规则时,应该先从匹配粒度最小的开始写
3.16.5.3.
P.MAC.DCL.03 不要在片段分类符跟随它不匹配的符号
3.16.5.4.
P.MAC.DCL.04 匹配规则要精准,不要模糊不清
3.16.5.5.
P.MAC.DCL.05 使用宏替换(substitution)元变量的时候要注意选择合适的片段分类符
3.16.5.6.
P.MAC.DCL.06 当宏需要接收 self 时需要注意
3.16.5.7.
P.MAC.DCL.07 确保在宏定义之后再去调用宏
3.16.5.8.
P.MAC.DCL.08 同一个 crate 内定义的宏相互调用时,需要注意卫生性
3.16.6.
过程宏
❱
3.16.6.1.
P.MAC.PRO.01 不要使用过程宏来规避静态分析检查
3.16.6.2.
P.MAC.PRO.02 实现过程宏时要对关键特性增加测试
3.16.6.3.
P.MAC.PRO.03 保证过程宏的卫生性
3.16.6.4.
P.MAC.PRO.04 给出正确的错误位置
3.17.
代码生成
❱
3.17.1.
P.CGN.01 代码生成要按情况选择使用过程宏还是 build.rs
3.17.2.
P.CGN.02 build.rs 生成的代码要保证没有任何警告
3.18.
多线程
❱
3.18.1.
锁同步
❱
3.18.1.1.
P.MTH.LCK.01 多线程下要注意识别锁争用的情况,避免死锁
3.18.1.2.
G.MTH.LCK.01 对布尔或引用并发访问应该使用原子类型而非互斥锁
3.18.1.3.
G.MTH.LCK.02 宜使用 Arc / Arc<[T]> 来代替 Arc / Arc<Vec>
3.18.1.4.
G.MTH.LCK.03 尽量避免直接使用标准库 std::sync 模块中的同步原语,替换为 parking_lot
3.18.1.5.
G.MTH.LCK.04 尽量避免直接使用标准库 std::sync::mpsc 模块中的 channel,替换为 crossbeam
3.18.2.
无锁
❱
3.18.2.1.
P.MTH.LKF.01 除非必要,否则建议使用同步锁
3.18.2.2.
P.MTH.LKF.02 使用无锁编程时,需要合理选择内存顺序
3.19.
异步编程
❱
3.19.1.
P.ASY.01 异步编程并不适合所有场景,计算密集型场景应该考虑同步编程
3.19.2.
G.ASY.01 在 async 块或函数中调用 async 函数或闭包请不要忘记添加.await
3.19.3.
G.ASY.02 在跨 await 调用中,需要对其持有的同步互斥锁进行处理
3.19.4.
G.ASY.03 在跨 await 调用中,需要对其持有 RefCell 的引用进行处理
3.19.5.
G.ASY.04 避免定义不必要的异步函数
3.19.6.
G.ASY.05 避免在异步处理过程中包含阻塞操作
3.20.
Unsafe Rust
❱
3.20.1.
P.UNS.01 不要为了逃避编译器安全检查而滥用 Unsafe Rust
3.20.2.
P.UNS.02 不要为了提升性能而盲目使用 Unsafe Rust
3.20.3.
G.UNS.01 不宜为带有 unsafe 命名的类型或方法创建别名
3.20.4.
安全抽象
❱
3.20.4.1.
P.UNS.SAS.01 代码中要注意是否会因为 Panic 发生而导致内存安全问题
3.20.4.2.
P.UNS.SAS.02 Unsafe 代码编写者有义务检查代码是否满足安全不变式
3.20.4.3.
P.UNS.SAS.03 不要随便在公开的 API 中暴露未初始化内存
3.20.4.4.
P.UNS.SAS.04 避免因为 Panic Safety 而导致双重释放
3.20.4.5.
P.UNS.SAS.05 手动实现 auto trait 时要充分考虑其安全性
3.20.4.6.
P.UNS.SAS.06 不要随便在公开的 API 中暴露裸指针
3.20.4.7.
P.UNS.SAS.07 在抽象安全方法的同时,也建议为性能考虑而增加相应的 Unsafe 方法
3.20.4.8.
P.UNS.SAS.08 函数参数是不可变借用的时候,返回值不应该是可变借用
3.20.4.9.
P.UNS.SAS.09 在任何 Unsafe 块之前都应该加 SAFETY 注释
3.20.4.10.
G.UNS.SAS.01 在公开的 unsafe 函数的文档中必须增加 Safety 注释
3.20.4.11.
G.UNS.SAS.02 在 Unafe 函数中应使用 assert! 而非 debug_assert! 去校验边界条件
3.20.5.
裸指针操作
❱
3.20.5.1.
P.UNS.PTR.01 不要将裸指针在多线程间共享
3.20.5.2.
P.UNS.PTR.02 建议使用 NonNull<T> 来替代 *mut T
3.20.5.3.
P.UNS.PTR.03 使用指针类型构造泛型结构体时,需要使用 PhantomData<T> 来指定 T上的协变和所有权
3.20.5.4.
G.UNS.PTR.01 当指针类型被强转为和当前内存对齐不一致的指针类型时,禁止对其解引用
3.20.5.5.
G.UNS.PTR.02 禁止将不可变指针手工转换为可变指针
3.20.5.6.
G.UNS.PTR.03 尽量使用 pointer::cast 来代替 使用 as 强转指针
3.20.6.
联合体
❱
3.20.6.1.
P.UNS.UNI.01 除了与 C 交互,尽量不要使用 Union
3.20.6.2.
P.UNS.UNI.02 不要把联合体的不同变体用在不同生命周期内
3.20.7.
内存
❱
3.20.7.1.
P.UNS.MEM.01 要注意选择合适的结构体、元组、枚举的数据布局
3.20.7.2.
P.UNS.MEM.02 不能修改其它进程或动态库的内存变量
3.20.7.3.
P.UNS.MEM.03 不能让 String/Vec 自动 Drop 其它进程或动态库的内存数据
3.20.7.4.
P.UNS.MEM.04 尽量用可重入(reentrant)版本的 C-API 或系统调用
3.20.7.5.
P.UNS.MEM.05 如果需要使用位域,推荐使用第三方库
3.20.7.6.
G.UNS.MEM.01 使用 MaybeUninit<T> 来处理未初始化的内存
3.20.8.
FFi
❱
3.20.8.1.
P.UNS.FFI.01 避免从公开的 Rust API 直接传字符串到 C 中
3.20.8.2.
P.UNS.FFI.02 在使用标准库 std::ffi 模块提供的类型时需要仔细查看其文档
3.20.8.3.
P.UNS.FFI.03 当使用来自 C 的指针时,如果该指针需要管理内存,则需要为包装该指针的 Rust 类型实现 Drop 特质
3.20.8.4.
P.UNS.FFI.04 如果一个函数正在跨越 FFi 边界,那么需要处理 Panic
3.20.8.5.
P.UNS.FFI.05 建议使用诸如标准库或 libc crate 所提供的可移植类型别名,而不是特定平台的类型
3.20.8.6.
P.UNS.FFI.06 Rust 和 C 之间传递字符或字符串时需要注意字符串要符合 C-ABI 以及 字符串的编码
3.20.8.7.
P.UNS.FFI.07 不要为任何传出外部的类型实现 Drop
3.20.8.8.
P.UNS.FFI.08 FFi 中要进行合理的错误处理
3.20.8.9.
P.UNS.FFI.09 当 Rust 调用外部 C 函数时,如果可以确认安全,可以通过引用来代替裸指针
3.20.8.10.
P.UNS.FFI.10 当 Rust 函数导出外部函数时,必须从设计上保证被跨线程调用的安全性
3.20.8.11.
P.UNS.FFI.11 如需引用指定为 #[repr(packed)] 内存布局的结构体成员字段要注意合理规避未定义行为
3.20.8.12.
P.UNS.FFI.12 当依赖 C 端传入参数时,需要在文档注释中不变性声明,根据不同的调用场景选择合适的安全抽象方式
3.20.8.13.
P.UNS.FFI.13 自定义数据类型要保证一致的数据布局
3.20.8.14.
P.UNS.FFI.14 在 FFi 中使用的类型应该拥有稳定布局
3.20.8.15.
P.UNS.FFI.15 从外部传入的不健壮类型的外部值要进行检查
3.20.8.16.
P.UNS.FFI.16 给 C 接口传递 Rust 闭包时需要将数据和代码进行分离,并要保证其安全性
3.20.8.17.
P.UNS.FFI.17 当Rust绑定C-API不透明(Opaque)类型时,应该使用指向专用不透明类型的指针而不是c_void指针
3.20.8.18.
P.UNS.FFI.18 避免将 trait 对象传递给 C 接口
3.20.9.
I/O
❱
3.20.9.1.
P.UNS.FIO.01 在使用原始句柄的时候,要注意 I/O 安全性
3.20.10.
Unsafe 代码术语指南
3.21.
no-std
❱
3.21.1.
P.EMB.01 no-std 下必须定义一个Panic行为以确保安全
3.21.2.
P.EMB.02 no-std 下要确保程序中的类型有正确的内存布局
3.22.
I/O
❱
3.22.1.
P.FIO.01 使用 read_to_end/read_to_string方法时注意文件的大小能否一次性读入内存中
3.22.2.
G.FIO.01 文件读取建议使用 BufReader/BufWriter 来代替 Reader/Write
3.23.
信息安全
❱
3.23.1.
P.SEC.01 使用第三方库的时候要确保可信的依赖,小心供应链攻击
3.23.2.
G.SEC.01 代码中不要出现非法 Unicode 字符,也要防范非法 Unicode 字符
3.24.
其他
❱
3.24.1.
G.OTH.01 对于某些场景下不建议使用的方法可以通过配置 clippy.toml 来拒绝
3.24.2.
G.OTH.01 使用标准库中对应的方法计算秒级、毫秒级、微秒级的时间
4.
附录
❱
4.1.
A.开发环境
4.2.
B.测试
❱
4.2.1.
单元测试
4.2.2.
基准测试
4.2.3.
模糊测试
4.3.
C.术语解释
4.4.
D.模板
❱
4.4.1.
rustfmt 模板
4.4.2.
clippy 模板
4.4.3.
deny 模板
4.5.
E.工具链
❱
4.5.1.
rustfmt
4.5.2.
noisy-clippy
4.5.3.
cargo-udeps
4.6.
F.Cheat Sheet
❱
4.6.1.
浮点数
4.7.
G.优化指南
4.8.
H.编译参数说明
4.9.
I.最佳实践
❱
4.9.1.
初学者常见问题Q&A
4.9.2.
Rust 编程技巧
4.10.
J.贡献说明
4.11.
K.淘汰的规则
Light (default)
Rust
Coal
Navy
Ayu
Rust 编码规范 V 1.0 beta
P.UNS.FFI.09 当 Rust 调用外部 C 函数时,如果可以确认安全,可以通过引用来代替裸指针
【描述】
在确认安全的前提下,在声明外部 C 函数时可以直接使用引用形式, C 语言可以使用正确绑定。