Showing preview only (764K chars total). Download the full file or copy to clipboard to get everything.
Repository: Rust-Coding-Guidelines/rust-coding-guidelines-zh
Branch: main
Commit: 6b3fc48b285b
Files: 353
Total size: 489.1 KB
Directory structure:
gitextract_1j8v9haz/
├── .github/
│ └── workflows/
│ └── main.yml
├── .gitignore
├── Changelog.md
├── LICENSE
├── MakeFile
├── README.md
├── book.toml
├── mermaid-init.js
└── src/
├── SUMMARY.md
├── contribution.md
├── overview.md
└── safe-guides/
├── Appendix/
│ ├── best-practice/
│ │ ├── intro.md
│ │ ├── qa.md
│ │ └── tips.md
│ ├── cheat-sheet/
│ │ ├── Numbers/
│ │ │ └── float.md
│ │ └── README.md
│ ├── contribution.md
│ ├── dev_env.md
│ ├── old_guidelines.md
│ ├── optimizing/
│ │ └── intro.md
│ ├── rustc-flag.md
│ ├── templates/
│ │ ├── clippy.toml.md
│ │ ├── deny.toml.md
│ │ ├── intro.md
│ │ └── rustfmt.toml.md
│ ├── terms.md
│ ├── test/
│ │ ├── benchmark.md
│ │ ├── fuzz.md
│ │ └── unit_test.md
│ ├── test.md
│ ├── toc.md
│ └── tools/
│ ├── cargo-udeps.md
│ ├── intro.md
│ ├── noisy-clippy.md
│ └── rustfmt.md
├── code_style/
│ ├── comments/
│ │ ├── G.CMT.01.md
│ │ ├── G.CMT.02.md
│ │ ├── G.CMT.03.md
│ │ ├── P.CMT.01.md
│ │ ├── P.CMT.02.md
│ │ ├── P.CMT.03.md
│ │ ├── P.CMT.04.md
│ │ └── P.CMT.05.md
│ ├── comments.md
│ ├── fmt/
│ │ ├── P.FMT.01.md
│ │ ├── P.FMT.02.md
│ │ ├── P.FMT.03.md
│ │ ├── P.FMT.04.md
│ │ ├── P.FMT.05.md
│ │ ├── P.FMT.06.md
│ │ ├── P.FMT.07.md
│ │ ├── P.FMT.08.md
│ │ ├── P.FMT.09.md
│ │ ├── P.FMT.10.md
│ │ ├── P.FMT.11.md
│ │ ├── P.FMT.12.md
│ │ ├── P.FMT.13.md
│ │ ├── P.FMT.14.md
│ │ ├── P.FMT.15.md
│ │ └── P.FMT.16.md
│ ├── fmt.md
│ ├── naming/
│ │ ├── G.NAM.01.md
│ │ ├── G.NAM.02.md
│ │ ├── P.NAM.01.md
│ │ ├── P.NAM.02.md
│ │ ├── P.NAM.03.md
│ │ ├── P.NAM.04.md
│ │ ├── P.NAM.05.md
│ │ ├── P.NAM.06.md
│ │ ├── P.NAM.07.md
│ │ ├── P.NAM.08.md
│ │ └── P.NAM.09.md
│ └── naming.md
├── code_style.md
├── coding_practice/
│ ├── async-await/
│ │ ├── G.ASY.01.md
│ │ ├── G.ASY.02.md
│ │ ├── G.ASY.03.md
│ │ ├── G.ASY.04.md
│ │ ├── G.ASY.05.md
│ │ └── P.ASY.01.md
│ ├── async-await.md
│ ├── cargo/
│ │ ├── G.CAR.01.md
│ │ ├── G.CAR.02.md
│ │ ├── G.CAR.03.md
│ │ ├── G.CAR.04.md
│ │ ├── P.CAR.01.md
│ │ ├── P.CAR.02.md
│ │ ├── P.CAR.03.md
│ │ └── P.CAR.04.md
│ ├── cargo.md
│ ├── code-generation/
│ │ ├── P.CGN.01.md
│ │ └── P.CGN.02.md
│ ├── code-generation.md
│ ├── collections/
│ │ ├── G.CLT.01.md
│ │ └── P.CLT.01.md
│ ├── collections.md
│ ├── consts/
│ │ ├── G.CNS.01.md
│ │ ├── G.CNS.02.md
│ │ ├── G.CNS.03.md
│ │ ├── G.CNS.04.md
│ │ └── G.CNS.05.md
│ ├── consts.md
│ ├── control-flow/
│ │ ├── G.CTF.01.md
│ │ ├── G.CTF.02.md
│ │ ├── G.CTF.03.md
│ │ ├── G.CTF.04.md
│ │ ├── P.CTF.01.md
│ │ └── P.CTF.02.md
│ ├── control-flow.md
│ ├── data-type/
│ │ ├── G.TYP.01.md
│ │ ├── G.TYP.02.md
│ │ ├── G.TYP.03.md
│ │ ├── P.TYP.01.md
│ │ ├── array/
│ │ │ ├── G.TYP.ARR.01.md
│ │ │ ├── G.TYP.ARR.02.md
│ │ │ └── G.TYP.ARR.03.md
│ │ ├── array.md
│ │ ├── bool/
│ │ │ ├── G.TYP.BOL.01.md
│ │ │ ├── G.TYP.BOL.02.md
│ │ │ ├── G.TYP.BOL.03.md
│ │ │ ├── G.TYP.BOL.04.md
│ │ │ ├── G.TYP.BOL.05.md
│ │ │ ├── G.TYP.BOL.06.md
│ │ │ └── G.TYP.BOL.07.md
│ │ ├── bool.md
│ │ ├── char/
│ │ │ ├── G.TYP.CHR.01.md
│ │ │ ├── G.TYP.CHR.02.md
│ │ │ └── G.TYP.CHR.03.md
│ │ ├── char.md
│ │ ├── enum/
│ │ │ ├── G.TYP.ENM.01.md
│ │ │ ├── G.TYP.ENM.02.md
│ │ │ ├── G.TYP.ENM.03.md
│ │ │ ├── G.TYP.ENM.04.md
│ │ │ ├── G.TYP.ENM.05.md
│ │ │ ├── G.TYP.ENM.06.md
│ │ │ └── G.TYP.ENM.07.md
│ │ ├── enum.md
│ │ ├── float/
│ │ │ ├── G.TYP.FLT.01.md
│ │ │ ├── G.TYP.FLT.02.md
│ │ │ ├── G.TYP.FLT.03.md
│ │ │ ├── G.TYP.FLT.04.md
│ │ │ └── G.TYP.FLT.05.md
│ │ ├── float.md
│ │ ├── int/
│ │ │ ├── G.TYP.INT.01.md
│ │ │ ├── G.TYP.INT.02.md
│ │ │ └── G.TYP.INT.03.md
│ │ ├── int.md
│ │ ├── ref/
│ │ │ └── .keep
│ │ ├── ref.md
│ │ ├── slice/
│ │ │ ├── P.TYP.SLC.01.md
│ │ │ └── P.TYP.SLC.02.md
│ │ ├── slice.md
│ │ ├── struct/
│ │ │ ├── G.TYP.SCT.01.md
│ │ │ ├── G.TYP.SCT.02.md
│ │ │ ├── G.TYP.SCT.03.md
│ │ │ ├── P.TYP.SCT.01.md
│ │ │ └── P.TYP.SCT.02.md
│ │ ├── struct.md
│ │ ├── tuple/
│ │ │ └── G.TYP.TUP.01.md
│ │ ├── tuple.md
│ │ ├── unit/
│ │ │ └── .keep
│ │ ├── unit.md
│ │ ├── vec/
│ │ │ ├── G.TYP.VEC.01.md
│ │ │ ├── P.TYP.VEC.01.md
│ │ │ └── P.TYP.VEC.02.md
│ │ └── vec.md
│ ├── data-type.md
│ ├── error-handle/
│ │ ├── G.ERR.01.md
│ │ ├── G.ERR.02.md
│ │ ├── P.ERR.01.md
│ │ └── P.ERR.02.md
│ ├── error-handle.md
│ ├── expr/
│ │ ├── G.EXP.01.md
│ │ ├── G.EXP.02.md
│ │ ├── G.EXP.03.md
│ │ ├── G.EXP.04.md
│ │ ├── G.EXP.05.md
│ │ └── G.EXP.06.md
│ ├── expr.md
│ ├── fn-design/
│ │ ├── G.FUD.01.md
│ │ ├── G.FUD.02.md
│ │ ├── G.FUD.03.md
│ │ ├── G.FUD.04.md
│ │ ├── G.FUD.05.md
│ │ ├── G.FUD.06.md
│ │ ├── P.FUD.01.md
│ │ └── P.FUD.02.md
│ ├── fn-design.md
│ ├── generic/
│ │ ├── G.GEN.01.md
│ │ ├── G.GEN.02.md
│ │ ├── P.GEN.01.md
│ │ ├── P.GEN.02.md
│ │ ├── P.GEN.03.md
│ │ ├── P.GEN.04.md
│ │ └── P.GEN.05.md
│ ├── generic.md
│ ├── io/
│ │ ├── G.FIO.01.md
│ │ └── P.FIO.01.md
│ ├── io.md
│ ├── macros/
│ │ ├── G.MAC.01.md
│ │ ├── G.MAC.02.md
│ │ ├── P.MAC.01.md
│ │ ├── P.MAC.02.md
│ │ ├── decl/
│ │ │ ├── P.MAC.DCL.01.md
│ │ │ ├── P.MAC.DCL.02.md
│ │ │ ├── P.MAC.DCL.03.md
│ │ │ ├── P.MAC.DCL.04.md
│ │ │ ├── P.MAC.DCL.05.md
│ │ │ ├── P.MAC.DCL.06.md
│ │ │ ├── P.MAC.DCL.07.md
│ │ │ └── P.MAC.DCL.08.md
│ │ ├── decl.md
│ │ ├── proc/
│ │ │ ├── P.MAC.PRO.01.md
│ │ │ ├── P.MAC.PRO.02.md
│ │ │ ├── P.MAC.PRO.03.md
│ │ │ └── P.MAC.PRO.04.md
│ │ └── proc.md
│ ├── macros.md
│ ├── memory/
│ │ ├── box/
│ │ │ ├── G.MEM.BOX.01.md
│ │ │ ├── G.MEM.BOX.02.md
│ │ │ └── G.MEM.BOX.03.md
│ │ ├── box.md
│ │ ├── drop/
│ │ │ └── G.MEM.DRP.01.md
│ │ ├── drop.md
│ │ ├── lifetime/
│ │ │ ├── P.MEM.LFT.01.md
│ │ │ └── P.MEM.LFT.02.md
│ │ ├── lifetime.md
│ │ ├── smart-ptr/
│ │ │ └── P.MEM.SPT.01.md
│ │ └── smart-ptr.md
│ ├── memory.md
│ ├── module/
│ │ ├── G.MOD.01.md
│ │ ├── G.MOD.02.md
│ │ ├── G.MOD.03.md
│ │ ├── G.MOD.04.md
│ │ ├── G.MOD.05.md
│ │ ├── P.MOD.01.md
│ │ └── P.MOD.02.md
│ ├── module.md
│ ├── no-std/
│ │ ├── P.EMB.01.md
│ │ └── P.EMB.02.md
│ ├── no-std.md
│ ├── others/
│ │ ├── G.OTH.01.md
│ │ └── G.OTH.02.md
│ ├── others.md
│ ├── security/
│ │ ├── G.SEC.01.md
│ │ └── P.SEC.01.md
│ ├── security.md
│ ├── statics/
│ │ └── G.STV.01.md
│ ├── statics.md
│ ├── strings/
│ │ ├── G.STR.01.md
│ │ ├── G.STR.02.md
│ │ ├── G.STR.03.md
│ │ ├── G.STR.04.md
│ │ ├── G.STR.05.md
│ │ ├── P.STR.01.md
│ │ ├── P.STR.02.md
│ │ ├── P.STR.03.md
│ │ ├── P.STR.04.md
│ │ └── P.STR.05.md
│ ├── strings.md
│ ├── threads/
│ │ ├── lock/
│ │ │ ├── G.MTH.LCK.01.md
│ │ │ ├── G.MTH.LCK.02.md
│ │ │ ├── G.MTH.LCK.03.md
│ │ │ ├── G.MTH.LCK.04.md
│ │ │ └── P.MTH.LCK.01.md
│ │ ├── lock-free/
│ │ │ ├── P.MTH.LKF.01.md
│ │ │ └── P.MTH.LKF.02.md
│ │ ├── lock-free.md
│ │ └── lock.md
│ ├── threads.md
│ ├── traits/
│ │ ├── P.TRA.01.md
│ │ ├── std-builtin/
│ │ │ ├── G.TRA.BLN.01.md
│ │ │ ├── G.TRA.BLN.02.md
│ │ │ ├── G.TRA.BLN.03.md
│ │ │ ├── G.TRA.BLN.04.md
│ │ │ ├── G.TRA.BLN.05.md
│ │ │ ├── G.TRA.BLN.06.md
│ │ │ ├── G.TRA.BLN.07.md
│ │ │ ├── G.TRA.BLN.08.md
│ │ │ ├── G.TRA.BLN.09.md
│ │ │ ├── G.TRA.BLN.10.md
│ │ │ └── P.TRA.BLN.01.md
│ │ ├── std-builtin.md
│ │ ├── trait-object/
│ │ │ ├── P.TRA.OBJ.01.md
│ │ │ └── P.TRA.OBJ.02.md
│ │ └── trait-object.md
│ ├── traits.md
│ ├── unsafe_rust/
│ │ ├── G.UNS.01.md
│ │ ├── P.UNS.01.md
│ │ ├── P.UNS.02.md
│ │ ├── P.UNS.03.md
│ │ ├── ffi/
│ │ │ ├── P.UNS.FFI.01.md
│ │ │ ├── P.UNS.FFI.02.md
│ │ │ ├── P.UNS.FFI.03.md
│ │ │ ├── P.UNS.FFI.04.md
│ │ │ ├── P.UNS.FFI.05.md
│ │ │ ├── P.UNS.FFI.06.md
│ │ │ ├── P.UNS.FFI.07.md
│ │ │ ├── P.UNS.FFI.08.md
│ │ │ ├── P.UNS.FFI.09.md
│ │ │ ├── P.UNS.FFI.10.md
│ │ │ ├── P.UNS.FFI.11.md
│ │ │ ├── P.UNS.FFI.12.md
│ │ │ ├── P.UNS.FFI.13.md
│ │ │ ├── P.UNS.FFI.14.md
│ │ │ ├── P.UNS.FFI.15.md
│ │ │ ├── P.UNS.FFI.16.md
│ │ │ ├── P.UNS.FFI.17.md
│ │ │ └── P.UNS.FFI.18.md
│ │ ├── ffi.md
│ │ ├── glossary.md
│ │ ├── io/
│ │ │ └── P.UNS.FIO.01.md
│ │ ├── io.md
│ │ ├── mem/
│ │ │ ├── G.UNS.MEM.01.md
│ │ │ ├── P.UNS.MEM.01.md
│ │ │ ├── P.UNS.MEM.02.md
│ │ │ ├── P.UNS.MEM.03.md
│ │ │ ├── P.UNS.MEM.04.md
│ │ │ └── P.UNS.MEM.05.md
│ │ ├── mem.md
│ │ ├── raw_ptr/
│ │ │ ├── G.UNS.PTR.01.md
│ │ │ ├── G.UNS.PTR.02.md
│ │ │ ├── G.UNS.PTR.03.md
│ │ │ ├── P.UNS.PTR.01.md
│ │ │ ├── P.UNS.PTR.02.md
│ │ │ └── P.UNS.PTR.03.md
│ │ ├── raw_ptr.md
│ │ ├── safe_abstract/
│ │ │ ├── G.UNS.SAS.01.md
│ │ │ ├── G.UNS.SAS.02.md
│ │ │ ├── P.UNS.SAS.01.md
│ │ │ ├── P.UNS.SAS.02.md
│ │ │ ├── P.UNS.SAS.03.md
│ │ │ ├── P.UNS.SAS.04.md
│ │ │ ├── P.UNS.SAS.05.md
│ │ │ ├── P.UNS.SAS.06.md
│ │ │ ├── P.UNS.SAS.07.md
│ │ │ ├── P.UNS.SAS.08.md
│ │ │ └── P.UNS.SAS.09.md
│ │ ├── safe_abstract.md
│ │ ├── union/
│ │ │ ├── P.UNS.UNI.01.md
│ │ │ └── P.UNS.UNI.02.md
│ │ └── union.md
│ ├── unsafe_rust.md
│ ├── variables/
│ │ ├── G.VAR.01.md
│ │ ├── G.VAR.02.md
│ │ ├── G.VAR.03.md
│ │ ├── G.VAR.04.md
│ │ ├── P.VAR.01.md
│ │ └── P.VAR.02.md
│ └── variables.md
├── coding_practice.md
└── overview/
├── convention.md
└── why.md
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/workflows/main.yml
================================================
name: CI
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
# Add explicit permissions - this is crucial!
permissions:
contents: write
jobs:
build:
name: Build, Test and Deploy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Important for git history
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Install mdbook
run: |
mkdir bin
curl -sSL https://github.com/rust-lang/mdBook/releases/download/v0.4.14/mdbook-v0.4.14-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=bin
echo "$(pwd)/bin" >> $GITHUB_PATH
- run: mdbook build
- name: Deploy to GitHub Pages
uses: JamesIves/github-pages-deploy-action@v4
with:
folder: book
branch: gh-pages
token: ${{ secrets.PAT }} # Explicitly use the token
clean: true # Clean up old files
================================================
FILE: .gitignore
================================================
book
.DS_Store
================================================
FILE: Changelog.md
================================================
## V 0.2 评审版本发布
任何事情如果想做到恰到好处,都需要一个进化的过程。编码规范也不例外。
1. 统一了规范的文本格式与措辞。
2. 经过考量,将一些原则变为了规则,并增加了自定义lint的说明。
3. 删除了一些不需要放到规范中条目。
4. 为一些规则丰富和精简了很多代码示例。
5. 移除规范中引用的不符合`MIT/Apache/Mozilla public licenses` 的开源配置和代码示例。
## V 0.3 发布
改进:
- 将当前无法使用 Clippy 检查的规则(G)统一修改为了原则(P)
- 删除和修复一些条款
- 新增 信息安全 `P.SEC.01` 条款
## V 1.0 beta 发布
改进:
- 更新目录结构
- 对文字和代码整体做了一遍评审,改进文字描述和代码格式
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2021 Rust Coding Guidelines (Unofficial)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: MakeFile
================================================
.PHONY: deploy
init:
git worktree add -f /tmp/rustguidebook gh-pages
git worktree remove -f /tmp/rustguidebook
git worktree add -f /tmp/rustguidebook gh-pages
deploy: init
@echo "====> deploying to github"
mdbook build
rm -rf /tmp/rustguidebook/*
cp -rp book/* /tmp/rustguidebook/
cd /tmp/rustguidebook && \
git add -A && \
git commit -m "deployed on $(shell date) by ${USER}" && \
git push origin gh-pages
================================================
FILE: README.md
================================================
# Rust 编码规范 中文版
## 状态
- 《Rust 编码规范》目前为 V 1.0 beta 试行版,改进内容参考 [Changelog](./Changelog.md)
## 介绍
据了解,Rust 社区内有些公司和组织都各自维护着自己的编码规范。下面罗列了一些已经公开的:
- [官方|Rust API 编写指南](https://rust-lang.github.io/api-guidelines/about.html)
- [官方 | Rust Style Guide](https://github.com/rust-dev-tools/fmt-rfcs/blob/master/guide/guide.md)
- [Rust's Unsafe Code Guidelines Reference](https://rust-lang.github.io/unsafe-code-guidelines/)
- [法国国家信息安全局 | Rust 安全(Security)规范](https://anssi-fr.github.io/rust-guide)
- [Facebook Diem 项目 Rust 编码规范(已过期)](https://developers.diem.com/docs/core/coding-guidelines/)
- [Apache Teaclave 安全计算平台 | Rust 开发规范](https://teaclave.apache.org/docs/rust-guildeline/)
- [PingCAP | 编码风格指南(包括 Rust 和 Go 等)](https://github.com/pingcap/style-guide)
- [Google Fuchsia 操作系统 Rust 开发指南](https://fuchsia.dev/fuchsia-src/development/languages/rust)
- [RustAnalyzer 编码风格指南](https://github.com/rust-analyzer/rust-analyzer/blob/master/docs/dev/style.md)
- [使用 Rust 设计优雅的 API](https://deterministic.space/elegant-apis-in-rust.html)
- [Rust FFI 指南](https://michael-f-bryan.github.io/rust-ffi-guide/)
- [大约 478 条 Clippy lint](https://rust-lang.github.io/rust-clippy/master/index.html)
- [lints in the rustc book ](https://doc.rust-lang.org/rustc/lints/listing/allowed-by-default.html)
- [Dtolnay 对 crates.io 中 clippy lint 应用统计](https://github.com/dtolnay/noisy-clippy)
但是随着 Rust 日益普及,统一的编码规范需求也逐渐浮出水面,本规范应运而生。

本规范致力于成为统一的 Rust 编码规范,并联合国内外公司共同维护。
公司可以依赖本规范,结合自己的业务领域和团队习惯,形成自己的编码规范,并可以在日常实践中反哺本规范,让本规范更加完善。
================================================
FILE: book.toml
================================================
[book]
authors = ["blackanger"]
language = "zh"
multilingual = false
src = "src"
title = "Rust 编码规范 V 1.0 beta"
[build]
create-missing = true
[preprocessor.toc]
command = "mdbook-toc"
renderer = ["html"]
max-level = 2
[preprocessor.mermaid]
command = "mdbook-mermaid"
[output.html]
git-repository-url = "https://github.com/Rust-Coding-Guidelines/rust-coding-guidelines-zh"
additional-js = ["mermaid.min.js", "mermaid-init.js"]
[output.html.fold]
enable = true
level = 0
================================================
FILE: mermaid-init.js
================================================
mermaid.initialize({startOnLoad:true});
================================================
FILE: src/SUMMARY.md
================================================
# Rust 编码规范
- [概述](./overview.md)
- [为什么需要 Rust 编码规范](./safe-guides/overview/why.md)
- [编码规范基本约定](./safe-guides/overview/convention.md)
- [代码风格](./safe-guides/code_style.md)
- [命名](./safe-guides/code_style/naming.md)
- [P.NAM.01 同一个crate中标识符的命名规则应该使用统一的词序](./safe-guides/code_style/naming/P.NAM.01.md)
- [P.NAM.02 为 cargo feature 命名时不应含有无意义的占位词](./safe-guides/code_style/naming/P.NAM.02.md)
- [P.NAM.03 标识符命名应该符合阅读习惯](./safe-guides/code_style/naming/P.NAM.03.md)
- [P.NAM.04 作用域越大命名越精确,反之应简短](./safe-guides/code_style/naming/P.NAM.04.md)
- [P.NAM.05 用于访问或获取数据的 getter 类方法通常不要使用 get_ 前缀](./safe-guides/code_style/naming/P.NAM.05.md)
- [P.NAM.06 遵循 iter/ iter_mut/ into_iter 规范来生成迭代器](./safe-guides/code_style/naming/P.NAM.06.md)
- [P.NAM.07 避免使用语言内置保留字、关键字、内置类型和trait等特殊名称](./safe-guides/code_style/naming/P.NAM.07.md)
- [P.NAM.08 避免在变量的命名中添加类型标识](./safe-guides/code_style/naming/P.NAM.08.md)
- [P.NAM.09 定义全局静态变量时需加前缀G_以便和常量有所区分](./safe-guides/code_style/naming/P.NAM.09.md)
- [G.NAM.01 使用统一的命名风格](./safe-guides/code_style/naming/G.NAM.01.md)
- [G.NAM.02 类型转换函数命名需要遵循所有权语义](./safe-guides/code_style/naming/G.NAM.02.md)
- [格式](./safe-guides/code_style/fmt.md)
- [P.FMT.01 使用 rustfmt 进行自动格式化代码](./safe-guides/code_style/fmt/P.FMT.01.md)
- [P.FMT.02 缩进使用空格而非制表符](./safe-guides/code_style/fmt/P.FMT.02.md)
- [P.FMT.03 行间距最大宽度空一行](./safe-guides/code_style/fmt/P.FMT.03.md)
- [P.FMT.04 语言项(Item) 定义时左花括号(brace)位置应该与语言项保持同一行](./safe-guides/code_style/fmt/P.FMT.04.md)
- [P.FMT.05 存在多个标识符时应该保持块状(Block)缩进](./safe-guides/code_style/fmt/P.FMT.05.md)
- [P.FMT.06 当有多行表达式操作时,操作符应该置于行首](./safe-guides/code_style/fmt/P.FMT.06.md)
- [P.FMT.07 枚举变体和结构体字段都应左对齐](./safe-guides/code_style/fmt/P.FMT.07.md)
- [P.FMT.08 函数参数超过五个或导入模块个数超过四个需换行](./safe-guides/code_style/fmt/P.FMT.08.md)
- [P.FMT.09 不同的场景,使用不同的空格风格](./safe-guides/code_style/fmt/P.FMT.09.md)
- [P.FMT.10 match 分支应该具有良好的可读性](./safe-guides/code_style/fmt/P.FMT.10.md)
- [P.FMT.11 导入模块分组应该具有良好的可读性](./safe-guides/code_style/fmt/P.FMT.11.md)
- [P.FMT.12 声明宏分支应该具有良好的可读性](./safe-guides/code_style/fmt/P.FMT.12.md)
- [P.FMT.13 具名结构体字段初始化时不要省略字段名](./safe-guides/code_style/fmt/P.FMT.13.md)
- [P.FMT.14 extern 外部函数需要显式指定 C-ABI](./safe-guides/code_style/fmt/P.FMT.14.md)
- [P.FMT.15 解构元组的时候允许使用..来指代剩余元素](./safe-guides/code_style/fmt/P.FMT.15.md)
- [P.FMT.16 不要将派生宏中多个不相关的特质合并为同一行](./safe-guides/code_style/fmt/P.FMT.16.md)
- [注释](./safe-guides/code_style/comments.md)
- [P.CMT.01 代码能做到自注释,文档要干练简洁](./safe-guides/code_style/comments/P.CMT.01.md)
- [P.CMT.02 注释应该有宽度限制](./safe-guides/code_style/comments/P.CMT.02.md)
- [P.CMT.03 使用行注释而避免使用块注释](./safe-guides/code_style/comments/P.CMT.03.md)
- [P.CMT.04 文件头注释包含版权说明](./safe-guides/code_style/comments/P.CMT.04.md)
- [P.CMT.05 在注释中使用 FIXME 和 TODO 来帮助任务协作](./safe-guides/code_style/comments/P.CMT.05.md)
- [G.CMT.01 在公开的返回Result类型的函数文档中增加 Error 注释](./safe-guides/code_style/comments/G.CMT.01.md)
- [G.CMT.02 如果公开的API在某些情况下会发生Panic,则相应文档中需增加 Panic 注释](./safe-guides/code_style/comments/G.CMT.02.md)
- [G.CMT.03 在文档注释中要使用空格代替 tab](./safe-guides/code_style/comments/G.CMT.03.md)
- [编码实践](./safe-guides/coding_practice.md)
- [常量](./safe-guides/coding_practice/consts.md)
- [G.CNS.01 对于科学计算中涉及浮点数近似值的常量宜使用预定义常量](./safe-guides/coding_practice/consts/G.CNS.01.md)
- [G.CNS.02 不应断言常量布尔类型](./safe-guides/coding_practice/consts/G.CNS.02.md)
- [G.CNS.03 不应将内部可变性容器声明为常量](./safe-guides/coding_practice/consts/G.CNS.03.md)
- [G.CNS.04 不应在常量定义中增加显式的 'static 生命周期](./safe-guides/coding_practice/consts/G.CNS.04.md)
- [G.CNS.05 对于适用 const fn 的函数或方法宜尽可能地使用 const fn](./safe-guides/coding_practice/consts/G.CNS.05.md)
- [静态变量](./safe-guides/coding_practice/statics.md)
- [G.STV.01 不宜直接使用可变静态变量作为全局变量](./safe-guides/coding_practice/statics/G.STV.01.md)
- [本地变量](./safe-guides/coding_practice/variables.md)
- [P.VAR.01 一般情况下避免先声明可变变量再赋值](./safe-guides/coding_practice/variables/P.VAR.01.md)
- [P.VAR.02 利用变量遮蔽功能保证变量安全使用](./safe-guides/coding_practice/variables/P.VAR.02.md)
- [G.VAR.01 以解构元组方式定义超过四个变量时不应使用太多无意义变量名](./safe-guides/coding_practice/variables/G.VAR.01.md)
- [G.VAR.02 不应使用非 ASCII 字符作为标识符](./safe-guides/coding_practice/variables/G.VAR.02.md)
- [G.VAR.03 变量遮蔽功能应当合理使用](./safe-guides/coding_practice/variables/G.VAR.03.md)
- [G.VAR.04 避免因局部变量过大而导致的大量栈分配](./safe-guides/coding_practice/variables/G.VAR.04.md)
- [数据类型](./safe-guides/coding_practice/data-type.md)
- [P.TYP.01 必要时,应使类型可以表达更明确的语义,而不是只是直接使用原生类型](./safe-guides/coding_practice/data-type/P.TYP.01.md)
- [G.TYP.01 类型转换尽可能使用安全的转换函数代替 as](./safe-guides/coding_practice/data-type/G.TYP.01.md)
- [G.TYP.02 数字字面量在使用的时候应该明确标注类型](./safe-guides/coding_practice/data-type/G.TYP.02.md)
- [G.TYP.03 不要用数字类型边界值判断能否安全转换,而应使用 try_from 方法](./safe-guides/coding_practice/data-type/G.TYP.03.md)
- [布尔](./safe-guides/coding_practice/data-type/bool.md)
- [G.TYP.BOL.01 不应将布尔值和布尔字面量进行比较](./safe-guides/coding_practice/data-type/bool/G.TYP.BOL.01.md)
- [G.TYP.BOL.02 如果 match 匹配表达式为布尔类型,宜使用 if 表达式来代替](./safe-guides/coding_practice/data-type/bool/G.TYP.BOL.02.md)
- [G.TYP.BOL.03 不应将数字类型转换为布尔值](./safe-guides/coding_practice/data-type/bool/G.TYP.BOL.03.md)
- [G.TYP.BOL.04 禁止在if表达式条件中使用块结构](./safe-guides/coding_practice/data-type/bool/G.TYP.BOL.04.md)
- [G.TYP.BOL.05 非必要时,布尔运算应使用逻辑运算符( &&/||)而非位运算符 (&/|)](./safe-guides/coding_practice/data-type/bool/G.TYP.BOL.05.md)
- [G.TYP.BOL.06 不应使用数字代替布尔值](./safe-guides/coding_practice/data-type/bool/G.TYP.BOL.06.md)
- [G.TYP.BOL.07 使用 `.not()` 方法代替逻辑取反运算符 (`!`)](./safe-guides/coding_practice/data-type/bool/G.TYP.BOL.07.md)
- [字符](./safe-guides/coding_practice/data-type/char.md)
- [G.TYP.CHR.01 不应将字符字面量强制转换为 u8](./safe-guides/coding_practice/data-type/char/G.TYP.CHR.01.md)
- [G.TYP.CHR.02 字符串方法中如果需要单个字符的值作为参数,宜使用字符而非字符串](./safe-guides/coding_practice/data-type/char/G.TYP.CHR.02.md)
- [G.TYP.CHR.03 需要将整数转换为字符时,应使用安全转换函数,而非 transmute](./safe-guides/coding_practice/data-type/char/G.TYP.CHR.03.md)
- [整数](./safe-guides/coding_practice/data-type/int.md)
- [G.TYP.INT.01 在用整数计算的时候需要考虑整数溢出、回绕和截断的风险](./safe-guides/coding_practice/data-type/int/G.TYP.INT.01.md)
- [G.TYP.INT.02 避免在有符号整数和无符号整数之间进行强制转换](./safe-guides/coding_practice/data-type/int/G.TYP.INT.02.md)
- [G.TYP.INT.03 对负数取模计算的时候不应使用 %](./safe-guides/coding_practice/data-type/int/G.TYP.INT.03.md)
- [浮点数](./safe-guides/coding_practice/data-type/float.md)
- [G.TYP.FLT.01 使用浮点数字面量时,要警惕是否存在被Rust编译器截断的风险](./safe-guides/coding_practice/data-type/float/G.TYP.FLT.01.md)
- [G.TYP.FLT.02 从任何数字类型转换为浮点类型时注意避免损失精度](./safe-guides/coding_practice/data-type/float/G.TYP.FLT.02.md)
- [G.TYP.FLT.03 对精度高要求的场景下,使用浮点数进行运算和比较时需要注意精度损失](./safe-guides/coding_practice/data-type/float/G.TYP.FLT.03.md)
- [G.TYP.FLT.04 宜使用Rust内置方法处理浮点数计算](./safe-guides/coding_practice/data-type/float/G.TYP.FLT.04.md)
- [G.TYP.FLT.05 禁止在浮点数和整数相互转换时使用 transmute](./safe-guides/coding_practice/data-type/float/G.TYP.FLT.05.md)
- [切片](./safe-guides/coding_practice/data-type/slice.md)
- [P.TYP.SLC.01 宜使用切片迭代器来代替手工索引](./safe-guides/coding_practice/data-type/slice/P.TYP.SLC.01.md)
- [P.TYP.SLC.02 宜使用切片模式来提升代码的可读性](./safe-guides/coding_practice/data-type/slice/P.TYP.SLC.02.md)
- [元组](./safe-guides/coding_practice/data-type/tuple.md)
- [G.TYP.TUP.01 使用元组时,其元素不宜超过3个](./safe-guides/coding_practice/data-type/tuple/G.TYP.TUP.01.md)
- [固定长度数组](./safe-guides/coding_practice/data-type/array.md)
- [G.TYP.ARR.01 创建大全局数组时宜使用静态变量而非常量](./safe-guides/coding_practice/data-type/array/G.TYP.ARR.01.md)
- [G.TYP.ARR.02 使用数组索引时禁止越界访问](./safe-guides/coding_practice/data-type/array/G.TYP.ARR.02.md)
- [G.TYP.ARR.03 当数组元素为原生数据类型(Primitive),排序时优先选用非稳定排序](./safe-guides/coding_practice/data-type/array/G.TYP.ARR.03.md)
- [动态数组](./safe-guides/coding_practice/data-type/vec.md)
- [P.TYP.VEC.01 非必要时不宜使用动态数组](./safe-guides/coding_practice/data-type/vec/P.TYP.VEC.01.md)
- [P.TYP.VEC.02 创建动态数组时,宜预先分配足够容量,避免后续操作中产生多次分配](./safe-guides/coding_practice/data-type/vec/P.TYP.VEC.02.md)
- [G.TYP.VEC.01 禁止访问未初始化的数组](./safe-guides/coding_practice/data-type/vec/G.TYP.VEC.01.md)
- [结构体](./safe-guides/coding_practice/data-type/struct.md)
- [P.TYP.SCT.01 为结构体实现构造性方法时,避免构造后再初始化的情况](./safe-guides/coding_practice/data-type/struct/P.TYP.SCT.01.md)
- [P.TYP.SCT.02 结构体实例需要默认实现时,宜使用Default特质](./safe-guides/coding_practice/data-type/struct/P.TYP.SCT.02.md)
- [G.TYP.SCT.01 对外导出的公开的 Struct,宜添加#[non_exhaustive]属性](./safe-guides/coding_practice/data-type/struct/G.TYP.SCT.01.md)
- [G.TYP.SCT.02 当结构体中有超过三个布尔类型的字段,宜将其独立为新的枚举类](./safe-guides/coding_practice/data-type/struct/G.TYP.SCT.02.md)
- [G.TYP.SCT.03 宜使用结构体功能更新语法来提升代码可读性](./safe-guides/coding_practice/data-type/struct/G.TYP.SCT.03.md)
- [枚举体](./safe-guides/coding_practice/data-type/enum.md)
- [G.TYP.ENM.01 合理使用map和and_then方法](./safe-guides/coding_practice/data-type/enum/G.TYP.ENM.01.md)
- [G.TYP.ENM.02 不应自行创建空枚举](./safe-guides/coding_practice/data-type/enum/G.TYP.ENM.02.md)
- [G.TYP.ENM.03 在使用类似 C 语言的枚举写法且使用repr(isize/usize) 布局时注意 32位架构上截断的问题](./safe-guides/coding_practice/data-type/enum/G.TYP.ENM.03.md)
- [G.TYP.ENM.04 不宜在use语句中引入Enum的全部变体(variants)](./safe-guides/coding_practice/data-type/enum/G.TYP.ENM.04.md)
- [G.TYP.ENM.05 对外导出的公开Enum,宜添加#[non_exhaustive]属性](./safe-guides/coding_practice/data-type/enum/G.TYP.ENM.05.md)
- [G.TYP.ENM.06 Enum内变体的大小差异不宜过大](./safe-guides/coding_practice/data-type/enum/G.TYP.ENM.06.md)
- [G.TYP.ENM.07 如需依赖 Enum 中变体的序数,则应显示为变体设置明确的数值](./safe-guides/coding_practice/data-type/enum/G.TYP.ENM.07.md)
- [表达式](./safe-guides/coding_practice/expr.md)
- [G.EXP.01 当需要对表达式求值后重新赋值时,宜使用复合赋值模式](./safe-guides/coding_practice/expr/G.EXP.01.md)
- [G.EXP.02 不宜在比较中使用不兼容的位掩码](./safe-guides/coding_practice/expr/G.EXP.02.md)
- [G.EXP.03 不应利用数组表达式的边界检查来 Panic,而应使用断言](./safe-guides/coding_practice/expr/G.EXP.03.md)
- [G.EXP.04 自增或自减运算使用`+=`或`-=`](./safe-guides/coding_practice/expr/G.EXP.04.md)
- [G.EXP.05 使用括号来清楚表示表达式的计算顺序](./safe-guides/coding_practice/expr/G.EXP.05.md)
- [G.EXP.06 避免在比较中添加无用的掩码操作](./safe-guides/coding_practice/expr/G.EXP.06.md)
- [控制流程](./safe-guides/coding_practice/control-flow.md)
- [P.CTF.01 避免滥用迭代器](./safe-guides/coding_practice/control-flow/P.CTF.01.md)
- [P.CTF.02 优先使用模式匹配而非判断后再取值](./safe-guides/coding_practice/control-flow/P.CTF.02.md)
- [G.CTF.01 当需要通过多个if判断来比较大小来区分不同情况时,优先使用match和cmp来代替if表达式](./safe-guides/coding_practice/control-flow/G.CTF.01.md)
- [G.CTF.02 if条件表达式分支中如果包含了else if分支也应该包含else分支](./safe-guides/coding_practice/control-flow/G.CTF.02.md)
- [G.CTF.03 如果要通过 if 条件表达式来判断是否 Panic,请优先使用断言](./safe-guides/coding_practice/control-flow/G.CTF.03.md)
- [G.CTF.04 在 Match 分支的 Guard 语句中不要使用带有副作用的条件表达式](./safe-guides/coding_practice/control-flow/G.CTF.04.md)
- [字符串](./safe-guides/coding_practice/strings.md)
- [P.STR.01 处理字符串元素时优先按字节处理而非字符](./safe-guides/coding_practice/strings/P.STR.01.md)
- [P.STR.02 创建字符串时,宜预先分配大约足够的容量来避免后续操作中产生多次分配](./safe-guides/coding_practice/strings/P.STR.02.md)
- [P.STR.03 在使用内建字符串处理函数或方法的时候,应注意避免隐藏的嵌套迭代或多次迭代](./safe-guides/coding_practice/strings/P.STR.03.md)
- [P.STR.04 在使用 `Cow<'a, B> `时要注意选择合理场景以便最大化地优化性能](./safe-guides/coding_practice/strings/P.STR.04.md)
- [P.STR.05 在拼接字符串时,优先使用format!](./safe-guides/coding_practice/strings/P.STR.05.md)
- [G.STR.01 在实现Display特质时不应调用to_string()方法](./safe-guides/coding_practice/strings/G.STR.01.md)
- [G.STR.02 在追加字符串时使用push_str方法](./safe-guides/coding_practice/strings/G.STR.02.md)
- [G.STR.03 将只包含 ASCII字符的字符串字面量转为字节序列可以直接使用b"str" 语法代替调用as_bytes方法](./safe-guides/coding_practice/strings/G.STR.03.md)
- [G.STR.04 需要辨别字符串的字符开头或结尾字符时,不应按字符迭代比较](./safe-guides/coding_practice/strings/G.STR.04.md)
- [G.STR.05 对字符串按指定位置进行切片的时候需要小心破坏其 UTF-8 编码](./safe-guides/coding_practice/strings/G.STR.05.md)
- [集合容器](./safe-guides/coding_practice/collections.md)
- [P.CLT.01 创建HashMap、VecDeque时,可以预先分配大约足够的容量来避免后续操作中产生多次分配](./safe-guides/coding_practice/collections/P.CLT.01.md)
- [G.CLT.01 非必要情况下,不要使用LinkedList,而用Vec或VecDeque代替](./safe-guides/coding_practice/collections/G.CLT.01.md)
- [函数设计](./safe-guides/coding_practice/fn-design.md)
- [P.FUD.01 传递到闭包的变量建议单独重新绑定](./safe-guides/coding_practice/fn-design/P.FUD.01.md)
- [P.FUD.02 函数返回值不要使用 return](./safe-guides/coding_practice/fn-design/P.FUD.02.md)
- [G.FUD.01 函数参数最长不要超过五个](./safe-guides/coding_practice/fn-design/G.FUD.01.md)
- [G.FUD.02 当函数参数实现了 Copy,并且是按值传入,如果值可能会太大,则宜考虑按引用传递](./safe-guides/coding_practice/fn-design/G.FUD.02.md)
- [G.FUD.03 当函数参数出现太多 bool 类型的参数时,应该考虑将其封装为自定义的结构体或枚举](./safe-guides/coding_practice/fn-design/G.FUD.03.md)
- [G.FUD.04 当Copy 类型的足够小的值作为函数参数时,应该按值(by-value)传入,而不是引用(by-ref)](./safe-guides/coding_practice/fn-design/G.FUD.04.md)
- [G.FUD.05 不要总是为函数指定 inline(always)](./safe-guides/coding_practice/fn-design/G.FUD.05.md)
- [G.FUD.06 函数参数应该考虑兼容多种类型](./safe-guides/coding_practice/fn-design/G.FUD.06.md)
- [泛型](./safe-guides/coding_practice/generic.md)
- [P.GEN.01 用泛型来抽象公共语义](./safe-guides/coding_practice/generic/P.GEN.01.md)
- [P.GEN.02 不要随便使用 impl Trait 语法替代泛型限定](./safe-guides/coding_practice/generic/P.GEN.02.md)
- [P.GEN.03 不要使用太多泛型参数和 trait 限定,否则会增长编译时间](./safe-guides/coding_practice/generic/P.GEN.03.md)
- [P.GEN.04 为泛型类型实现方法时,impl 中声明的泛型类型参数一定要被用到](./safe-guides/coding_practice/generic/P.GEN.04.md)
- [P.GEN.05 定义泛型函数时,如果该函数实现用到来自 trait 定义的相关行为,需要为泛型指定相关 trait 的限定](./safe-guides/coding_practice/generic/P.GEN.05.md)
- [G.GEN.01 不要在泛型位置上使用内建类型](./safe-guides/coding_practice/generic/G.GEN.01.md)
- [G.GEN.02 使用 Rust 标准库中某些方法,要注意避免使用其泛型默认实现,而应该使用具体类型的实现](./safe-guides/coding_practice/generic/G.GEN.02.md)
- [特质](./safe-guides/coding_practice/traits.md)
- [P.TRA.01 使用 trait 时要注意 trait 一致性规则](./safe-guides/coding_practice/traits/P.TRA.01.md)
- [标准库内置 trait](./safe-guides/coding_practice/traits/std-builtin.md)
- [P.TRA.BLN.01 在实现Borrow特质时,需要注意一致性](./safe-guides/coding_practice/traits/std-builtin/P.TRA.BLN.01.md)
- [G.TRA.BLN.01 应该具体类型的 default() 方法代替 Default::default() 调用](./safe-guides/coding_practice/traits/std-builtin/G.TRA.BLN.01.md)
- [G.TRA.BLN.02 不要为迭代器实现Copy 特质](./safe-guides/coding_practice/traits/std-builtin/G.TRA.BLN.02.md)
- [G.TRA.BLN.03 能使用派生宏(Derive)自动实现Default特质就不要用手工实现](./safe-guides/coding_practice/traits/std-builtin/G.TRA.BLN.03.md)
- [G.TRA.BLN.04 在使用#[derive(Hash)] 的时候,避免再手工实现 PartialEq](./safe-guides/coding_practice/traits/std-builtin/G.TRA.BLN.04.md)
- [G.TRA.BLN.05 在使用#[derive(Ord)] 的时候,避免再手工实现 PartialOrd](./safe-guides/coding_practice/traits/std-builtin/G.TRA.BLN.05.md)
- [G.TRA.BLN.06 不要对实现 Copy 或引用类型调用 std::mem::drop 和 std::mem::forgot](./safe-guides/coding_practice/traits/std-builtin/G.TRA.BLN.06.md)
- [G.TRA.BLN.07 对实现 Copy 的可迭代类型来说,要通过迭代器拷贝其所有元素时,应该使用 copied方法,而非cloned](./safe-guides/coding_practice/traits/std-builtin/G.TRA.BLN.07.md)
- [G.TRA.BLN.08 实现 From 而不是 Into](./safe-guides/coding_practice/traits/std-builtin/G.TRA.BLN.08.md)
- [G.TRA.BLN.09 一般情况下不要给 Copy 类型手工实现 Clone](./safe-guides/coding_practice/traits/std-builtin/G.TRA.BLN.09.md)
- [G.TRA.BLN.10 不要随便使用Deref特质来模拟继承](./safe-guides/coding_practice/traits/std-builtin/G.TRA.BLN.10.md)
- [trait 对象](./safe-guides/coding_practice/traits/trait-object.md)
- [P.TRA.OBJ.01 根据场景合理选择使用trait对象或泛型静态分发](./safe-guides/coding_practice/traits/trait-object/P.TRA.OBJ.01.md)
- [P.TRA.OBJ.02 除非必要,避免自定义虚表](./safe-guides/coding_practice/traits/trait-object/P.TRA.OBJ.02.md)
- [错误处理](./safe-guides/coding_practice/error-handle.md)
- [P.ERR.01 当传入函数的参数值因为超出某种限制可能会导致函数调用失败,应该使用断言](./safe-guides/coding_practice/error-handle/P.ERR.01.md)
- [P.ERR.02 在确定 Option<T> 和 Result<T, E>类型的值不可能是 None 或 Err 时,请用 expect 代替 unwrap()](./safe-guides/coding_practice/error-handle/P.ERR.02.md)
- [G.ERR.01 在处理 Option<T> 和 Result<T, E> 类型时,不要随便使用 unwrap](./safe-guides/coding_practice/error-handle/G.ERR.01.md)
- [G.ERR.02 不要滥用 expect,请考虑用 unwrap_or_ 系列方法代替](./safe-guides/coding_practice/error-handle/G.ERR.02.md)
- [内存管理](./safe-guides/coding_practice/memory.md)
- [生命周期](./safe-guides/coding_practice/memory/lifetime.md)
- [P.MEM.LFT.01 生命周期参数命名尽量有意义且简洁](./safe-guides/coding_practice/memory/lifetime/P.MEM.LFT.01.md)
- [P.MEM.LFT.02 通常需要显式地标注生命周期,而非利用编译器推断](./safe-guides/coding_practice/memory/lifetime/P.MEM.LFT.02.md)
- [智能指针](./safe-guides/coding_practice/memory/smart-ptr.md)
- [P.MEM.SPT.01 使用 `RefCell<T>` 时宜使用 `try_borrow`/`try_borrow_mut` 方法](./safe-guides/coding_practice/memory/smart-ptr/P.MEM.SPT.01.md)
- [Box 类型](./safe-guides/coding_practice/memory/box.md)
- [G.MEM.BOX.01 一般情况下,不应直接对 `Box<T>` 进行借用](./safe-guides/coding_practice/memory/box/G.MEM.BOX.01.md)
- [G.MEM.BOX.02 一般情况下,不应直接对已经在堆上分配内存的类型进行 Box 装箱](./safe-guides/coding_practice/memory/box/G.MEM.BOX.02.md)
- [G.MEM.BOX.03 一般情况下,不应直接对栈分配类型进行 Box 装箱](./safe-guides/coding_practice/memory/box/G.MEM.BOX.03.md)
- [Drop 析构](./safe-guides/coding_practice/memory/drop.md)
- [G.MEM.DRP.01 要注意防范内存泄漏](./safe-guides/coding_practice/memory/drop/G.MEM.DRP.01.md)
- [模块](./safe-guides/coding_practice/module.md)
- [P.MOD.01 合理控制对外接口和模块之间的可见性](./safe-guides/coding_practice/module/P.MOD.01.md)
- [P.MOD.02 将模块的测试移动到单独的文件,有助于增加编译速度](./safe-guides/coding_practice/module/P.MOD.02.md)
- [G.MOD.01 使用导入模块中的类型或函数,在某些情况下需要带模块名前缀](./safe-guides/coding_practice/module/G.MOD.01.md)
- [G.MOD.02 如果是作为库供别人使用,在 lib.rs中重新导出对外类型、函数和 trait 等](./safe-guides/coding_practice/module/G.MOD.02.md)
- [G.MOD.03 导入模块不要随便使用 通配符`*`](./safe-guides/coding_practice/module/G.MOD.03.md)
- [G.MOD.04 一个项目中应该避免使用不同的模块布局风格](./safe-guides/coding_practice/module/G.MOD.04.md)
- [G.MOD.05 不要在私有模块中设置其内部类型或函数方法为 pub(crate)](./safe-guides/coding_practice/module/G.MOD.05.md)
- [包管理](./safe-guides/coding_practice/cargo.md)
- [P.CAR.01 应该尽量把项目划分为合理的 crate 组合](./safe-guides/coding_practice/cargo/P.CAR.01.md)
- [P.CAR.02 不要滥用 Features](./safe-guides/coding_practice/cargo/P.CAR.02.md)
- [P.CAR.03 使用 cargo features 来代替 `--cfg` 条件编译参数](./safe-guides/coding_practice/cargo/P.CAR.03.md)
- [P.CAR.04 宜使用 `cfg!` 来代替 `#[cfg]`](./safe-guides/coding_practice/cargo/P.CAR.04.md)
- [G.CAR.01 当项目是可执行程序而非库时,建议使用 `src/main.rs` 和 `src/lib.rs` 模式](./safe-guides/coding_practice/cargo/G.CAR.01.md)
- [G.CAR.02 Crate 的 Cargo.toml 中应该包含必要的元信息](./safe-guides/coding_practice/cargo/G.CAR.02.md)
- [G.CAR.03 Feature 命名应该避免否定式或多余的前后缀](./safe-guides/coding_practice/cargo/G.CAR.03.md)
- [G.CAR.04 Cargo.toml 中依赖包版本不应使用通配符](./safe-guides/coding_practice/cargo/G.CAR.04.md)
- [宏](./safe-guides/coding_practice/macros.md)
- [P.MAC.01 不要轻易使用宏](./safe-guides/coding_practice/macros/P.MAC.01.md)
- [P.MAC.02 实现宏语法的时候,应该尽量贴近 Rust 语法](./safe-guides/coding_practice/macros/P.MAC.02.md)
- [G.MAC.01 `dbg!()` 宏只应该用于调试代码](./safe-guides/coding_practice/macros/G.MAC.01.md)
- [G.MAC.02 使用宏时应该考虑宏展开会让编译文件膨胀的影响](./safe-guides/coding_practice/macros/G.MAC.02.md)
- [声明宏](./safe-guides/coding_practice/macros/decl.md)
- [P.MAC.DCL.01 不要将声明宏内的变量作为外部变量使用](./safe-guides/coding_practice/macros/decl/P.MAC.DCL.01.md)
- [P.MAC.DCL.02 在编写多个宏规则时,应该先从匹配粒度最小的开始写](./safe-guides/coding_practice/macros/decl/P.MAC.DCL.02.md)
- [P.MAC.DCL.03 不要在片段分类符跟随它不匹配的符号](./safe-guides/coding_practice/macros/decl/P.MAC.DCL.03.md)
- [P.MAC.DCL.04 匹配规则要精准,不要模糊不清](./safe-guides/coding_practice/macros/decl/P.MAC.DCL.04.md)
- [P.MAC.DCL.05 使用宏替换(substitution)元变量的时候要注意选择合适的片段分类符](./safe-guides/coding_practice/macros/decl/P.MAC.DCL.05.md)
- [P.MAC.DCL.06 当宏需要接收 self 时需要注意](./safe-guides/coding_practice/macros/decl/P.MAC.DCL.06.md)
- [P.MAC.DCL.07 确保在宏定义之后再去调用宏](./safe-guides/coding_practice/macros/decl/P.MAC.DCL.07.md)
- [P.MAC.DCL.08 同一个 crate 内定义的宏相互调用时,需要注意卫生性](./safe-guides/coding_practice/macros/decl/P.MAC.DCL.08.md)
- [过程宏](./safe-guides/coding_practice/macros/proc.md)
- [P.MAC.PRO.01 不要使用过程宏来规避静态分析检查](./safe-guides/coding_practice/macros/proc/P.MAC.PRO.01.md)
- [P.MAC.PRO.02 实现过程宏时要对关键特性增加测试](./safe-guides/coding_practice/macros/proc/P.MAC.PRO.02.md)
- [P.MAC.PRO.03 保证过程宏的卫生性](./safe-guides/coding_practice/macros/proc/P.MAC.PRO.03.md)
- [P.MAC.PRO.04 给出正确的错误位置](./safe-guides/coding_practice/macros/proc/P.MAC.PRO.04.md)
- [代码生成](./safe-guides/coding_practice/code-generation.md)
- [P.CGN.01 代码生成要按情况选择使用过程宏还是 `build.rs`](./safe-guides/coding_practice/code-generation/P.CGN.01.md)
- [P.CGN.02 `build.rs` 生成的代码要保证没有任何警告](./safe-guides/coding_practice/code-generation/P.CGN.02.md)
- [多线程](./safe-guides/coding_practice/threads.md)
- [锁同步](./safe-guides/coding_practice/threads/lock.md)
- [P.MTH.LCK.01 多线程下要注意识别锁争用的情况,避免死锁](./safe-guides/coding_practice/threads/lock/P.MTH.LCK.01.md)
- [G.MTH.LCK.01 对布尔或引用并发访问应该使用原子类型而非互斥锁](./safe-guides/coding_practice/threads/lock/G.MTH.LCK.01.md)
- [G.MTH.LCK.02 宜使用 Arc<str> / Arc<[T]> 来代替 Arc<String> / Arc<Vec<T>>](./safe-guides/coding_practice/threads/lock/G.MTH.LCK.02.md)
- [G.MTH.LCK.03 尽量避免直接使用标准库 `std::sync` 模块中的同步原语,替换为 `parking_lot`](./safe-guides/coding_practice/threads/lock/G.MTH.LCK.03.md)
- [G.MTH.LCK.04 尽量避免直接使用标准库 `std::sync::mpsc` 模块中的 channel,替换为 `crossbeam`](./safe-guides/coding_practice/threads/lock/G.MTH.LCK.04.md)
- [无锁](./safe-guides/coding_practice/threads/lock-free.md)
- [P.MTH.LKF.01 除非必要,否则建议使用同步锁](./safe-guides/coding_practice/threads/lock-free/P.MTH.LKF.01.md)
- [P.MTH.LKF.02 使用无锁编程时,需要合理选择内存顺序](./safe-guides/coding_practice/threads/lock-free/P.MTH.LKF.02.md)
- [异步编程](./safe-guides/coding_practice/async-await.md)
- [P.ASY.01 异步编程并不适合所有场景,计算密集型场景应该考虑同步编程](./safe-guides/coding_practice/async-await/P.ASY.01.md)
- [G.ASY.01 在 `async` 块或函数中调用 `async` 函数或闭包请不要忘记添加`.await`](./safe-guides/coding_practice/async-await/G.ASY.01.md)
- [G.ASY.02 在跨 await 调用中,需要对其持有的同步互斥锁进行处理](./safe-guides/coding_practice/async-await/G.ASY.02.md)
- [G.ASY.03 在跨 await 调用中,需要对其持有 RefCell 的引用进行处理](./safe-guides/coding_practice/async-await/G.ASY.03.md)
- [G.ASY.04 避免定义不必要的异步函数](./safe-guides/coding_practice/async-await/G.ASY.04.md)
- [G.ASY.05 避免在异步处理过程中包含阻塞操作](./safe-guides/coding_practice/async-await/G.ASY.05.md)
- [Unsafe Rust](./safe-guides/coding_practice/unsafe_rust.md)
- [P.UNS.01 不要为了逃避编译器安全检查而滥用 Unsafe Rust](./safe-guides/coding_practice/unsafe_rust/P.UNS.01.md)
- [P.UNS.02 不要为了提升性能而盲目使用 Unsafe Rust](./safe-guides/coding_practice/unsafe_rust/P.UNS.02.md)
- [G.UNS.01 不宜为带有 unsafe 命名的类型或方法创建别名](./safe-guides/coding_practice/unsafe_rust/G.UNS.01.md)
- [安全抽象](./safe-guides/coding_practice/unsafe_rust/safe_abstract.md)
- [P.UNS.SAS.01 代码中要注意是否会因为 Panic 发生而导致内存安全问题](./safe-guides/coding_practice/unsafe_rust/safe_abstract/P.UNS.SAS.01.md)
- [P.UNS.SAS.02 Unsafe 代码编写者有义务检查代码是否满足安全不变式](./safe-guides/coding_practice/unsafe_rust/safe_abstract/P.UNS.SAS.02.md)
- [P.UNS.SAS.03 不要随便在公开的 API 中暴露未初始化内存](./safe-guides/coding_practice/unsafe_rust/safe_abstract/P.UNS.SAS.03.md)
- [P.UNS.SAS.04 避免因为 Panic Safety 而导致双重释放](./safe-guides/coding_practice/unsafe_rust/safe_abstract/P.UNS.SAS.04.md)
- [P.UNS.SAS.05 手动实现 auto trait 时要充分考虑其安全性](./safe-guides/coding_practice/unsafe_rust/safe_abstract/P.UNS.SAS.05.md)
- [P.UNS.SAS.06 不要随便在公开的 API 中暴露裸指针](./safe-guides/coding_practice/unsafe_rust/safe_abstract/P.UNS.SAS.06.md)
- [P.UNS.SAS.07 在抽象安全方法的同时,也建议为性能考虑而增加相应的 Unsafe 方法](./safe-guides/coding_practice/unsafe_rust/safe_abstract/P.UNS.SAS.07.md)
- [P.UNS.SAS.08 函数参数是不可变借用的时候,返回值不应该是可变借用](./safe-guides/coding_practice/unsafe_rust/safe_abstract/P.UNS.SAS.08.md)
- [P.UNS.SAS.09 在任何 Unsafe 块之前都应该加 SAFETY 注释](./safe-guides/coding_practice/unsafe_rust/safe_abstract/P.UNS.SAS.09.md)
- [G.UNS.SAS.01 在公开的 unsafe 函数的文档中必须增加 Safety 注释](./safe-guides/coding_practice/unsafe_rust/safe_abstract/G.UNS.SAS.01.md)
- [G.UNS.SAS.02 在 Unafe 函数中应使用 `assert!` 而非 `debug_assert!` 去校验边界条件](./safe-guides/coding_practice/unsafe_rust/safe_abstract/G.UNS.SAS.02.md)
- [裸指针操作](./safe-guides/coding_practice/unsafe_rust/raw_ptr.md)
- [P.UNS.PTR.01 不要将裸指针在多线程间共享](./safe-guides/coding_practice/unsafe_rust/raw_ptr/P.UNS.PTR.01.md)
- [P.UNS.PTR.02 建议使用 `NonNull<T>` 来替代 `*mut T`](./safe-guides/coding_practice/unsafe_rust/raw_ptr/P.UNS.PTR.02.md)
- [P.UNS.PTR.03 使用指针类型构造泛型结构体时,需要使用 `PhantomData<T>` 来指定 `T`上的协变和所有权](./safe-guides/coding_practice/unsafe_rust/raw_ptr/P.UNS.PTR.03.md)
- [G.UNS.PTR.01 当指针类型被强转为和当前内存对齐不一致的指针类型时,禁止对其解引用](./safe-guides/coding_practice/unsafe_rust/raw_ptr/G.UNS.PTR.01.md)
- [G.UNS.PTR.02 禁止将不可变指针手工转换为可变指针](./safe-guides/coding_practice/unsafe_rust/raw_ptr/G.UNS.PTR.02.md)
- [G.UNS.PTR.03 尽量使用 `pointer::cast` 来代替 使用 `as` 强转指针](./safe-guides/coding_practice/unsafe_rust/raw_ptr/G.UNS.PTR.03.md)
- [联合体](./safe-guides/coding_practice/unsafe_rust/union.md)
- [P.UNS.UNI.01 除了与 C 交互,尽量不要使用 Union](./safe-guides/coding_practice/unsafe_rust/union/P.UNS.UNI.01.md)
- [P.UNS.UNI.02 不要把联合体的不同变体用在不同生命周期内](./safe-guides/coding_practice/unsafe_rust/union/P.UNS.UNI.02.md)
- [内存](./safe-guides/coding_practice/unsafe_rust/mem.md)
- [P.UNS.MEM.01 要注意选择合适的结构体、元组、枚举的数据布局](./safe-guides/coding_practice/unsafe_rust/mem/P.UNS.MEM.01.md)
- [P.UNS.MEM.02 不能修改其它进程或动态库的内存变量](./safe-guides/coding_practice/unsafe_rust/mem/P.UNS.MEM.02.md)
- [P.UNS.MEM.03 不能让 `String/Vec` 自动 Drop 其它进程或动态库的内存数据](./safe-guides/coding_practice/unsafe_rust/mem/P.UNS.MEM.03.md)
- [P.UNS.MEM.04 尽量用可重入(reentrant)版本的 C-API 或系统调用](./safe-guides/coding_practice/unsafe_rust/mem/P.UNS.MEM.04.md)
- [P.UNS.MEM.05 如果需要使用位域,推荐使用第三方库](./safe-guides/coding_practice/unsafe_rust/mem/P.UNS.MEM.05.md)
- [G.UNS.MEM.01 使用 `MaybeUninit<T>` 来处理未初始化的内存](./safe-guides/coding_practice/unsafe_rust/mem/G.UNS.MEM.01.md)
- [FFi](./safe-guides/coding_practice/unsafe_rust/ffi.md)
- [P.UNS.FFI.01 避免从公开的 Rust API 直接传字符串到 C 中](./safe-guides/coding_practice/unsafe_rust/ffi/P.UNS.FFI.01.md)
- [P.UNS.FFI.02 在使用标准库 `std::ffi` 模块提供的类型时需要仔细查看其文档](./safe-guides/coding_practice/unsafe_rust/ffi/P.UNS.FFI.02.md)
- [P.UNS.FFI.03 当使用来自 C 的指针时,如果该指针需要管理内存,则需要为包装该指针的 Rust 类型实现 Drop 特质](./safe-guides/coding_practice/unsafe_rust/ffi/P.UNS.FFI.03.md)
- [P.UNS.FFI.04 如果一个函数正在跨越 FFi 边界,那么需要处理 Panic](./safe-guides/coding_practice/unsafe_rust/ffi/P.UNS.FFI.04.md)
- [P.UNS.FFI.05 建议使用诸如标准库或 `libc` crate 所提供的可移植类型别名,而不是特定平台的类型](./safe-guides/coding_practice/unsafe_rust/ffi/P.UNS.FFI.05.md)
- [P.UNS.FFI.06 Rust 和 C 之间传递字符或字符串时需要注意字符串要符合 C-ABI 以及 字符串的编码](./safe-guides/coding_practice/unsafe_rust/ffi/P.UNS.FFI.06.md)
- [P.UNS.FFI.07 不要为任何传出外部的类型实现 Drop](./safe-guides/coding_practice/unsafe_rust/ffi/P.UNS.FFI.07.md)
- [P.UNS.FFI.08 FFi 中要进行合理的错误处理](./safe-guides/coding_practice/unsafe_rust/ffi/P.UNS.FFI.08.md)
- [P.UNS.FFI.09 当 Rust 调用外部 C 函数时,如果可以确认安全,可以通过引用来代替裸指针](./safe-guides/coding_practice/unsafe_rust/ffi/P.UNS.FFI.09.md)
- [P.UNS.FFI.10 当 Rust 函数导出外部函数时,必须从设计上保证被跨线程调用的安全性](./safe-guides/coding_practice/unsafe_rust/ffi/P.UNS.FFI.10.md)
- [P.UNS.FFI.11 如需引用指定为 `#[repr(packed)]` 内存布局的结构体成员字段要注意合理规避未定义行为](./safe-guides/coding_practice/unsafe_rust/ffi/P.UNS.FFI.11.md)
- [P.UNS.FFI.12 当依赖 C 端传入参数时,需要在文档注释中不变性声明,根据不同的调用场景选择合适的安全抽象方式](./safe-guides/coding_practice/unsafe_rust/ffi/P.UNS.FFI.12.md)
- [P.UNS.FFI.13 自定义数据类型要保证一致的数据布局](./safe-guides/coding_practice/unsafe_rust/ffi/P.UNS.FFI.13.md)
- [P.UNS.FFI.14 在 FFi 中使用的类型应该拥有稳定布局](./safe-guides/coding_practice/unsafe_rust/ffi/P.UNS.FFI.14.md)
- [P.UNS.FFI.15 从外部传入的不健壮类型的外部值要进行检查](./safe-guides/coding_practice/unsafe_rust/ffi/P.UNS.FFI.15.md)
- [P.UNS.FFI.16 给 C 接口传递 Rust 闭包时需要将数据和代码进行分离,并要保证其安全性](./safe-guides/coding_practice/unsafe_rust/ffi/P.UNS.FFI.16.md)
- [P.UNS.FFI.17 当Rust绑定C-API不透明(Opaque)类型时,应该使用指向专用不透明类型的指针而不是`c_void`指针](./safe-guides/coding_practice/unsafe_rust/ffi/P.UNS.FFI.17.md)
- [P.UNS.FFI.18 避免将 trait 对象传递给 C 接口](./safe-guides/coding_practice/unsafe_rust/ffi/P.UNS.FFI.18.md)
- [I/O](./safe-guides/coding_practice/unsafe_rust/io.md)
- [P.UNS.FIO.01 在使用原始句柄的时候,要注意 `I/O` 安全性](./safe-guides/coding_practice/unsafe_rust/io/P.UNS.FIO.01.md)
- [Unsafe 代码术语指南](./safe-guides/coding_practice/unsafe_rust/glossary.md)
- [no-std](./safe-guides/coding_practice/no-std.md)
- [P.EMB.01 no-std 下必须定义一个Panic行为以确保安全](./safe-guides/coding_practice/no-std/P.EMB.01.md)
- [P.EMB.02 no-std 下要确保程序中的类型有正确的内存布局](./safe-guides/coding_practice/no-std/P.EMB.02.md)
- [I/O](./safe-guides/coding_practice/io.md)
- [P.FIO.01 使用 `read_to_end`/`read_to_string`方法时注意文件的大小能否一次性读入内存中](./safe-guides/coding_practice/io/P.FIO.01.md)
- [G.FIO.01 文件读取建议使用 `BufReader`/`BufWriter` 来代替 `Reader`/`Write`](./safe-guides/coding_practice/io/G.FIO.01.md)
- [信息安全](./safe-guides/coding_practice/security.md)
- [P.SEC.01 使用第三方库的时候要确保可信的依赖,小心供应链攻击](./safe-guides/coding_practice/security/P.SEC.01.md)
- [G.SEC.01 代码中不要出现非法 Unicode 字符,也要防范非法 Unicode 字符](./safe-guides/coding_practice/security/G.SEC.01.md)
- [其他](./safe-guides/coding_practice/others.md)
- [G.OTH.01 对于某些场景下不建议使用的方法可以通过配置 clippy.toml 来拒绝](./safe-guides/coding_practice/others/G.OTH.01.md)
- [G.OTH.01 使用标准库中对应的方法计算秒级、毫秒级、微秒级的时间](./safe-guides/coding_practice/others/G.OTH.02.md)
- [附录](./safe-guides/Appendix/toc.md)
- [A.开发环境](./safe-guides/Appendix/dev_env.md)
- [B.测试](./safe-guides/Appendix/test.md)
- [单元测试](./safe-guides/Appendix/test/unit_test.md)
- [基准测试](./safe-guides/Appendix/test/benchmark.md)
- [模糊测试](./safe-guides/Appendix/test/fuzz.md)
- [C.术语解释](./safe-guides/Appendix/terms.md)
- [D.模板](./safe-guides/Appendix/templates/intro.md)
- [rustfmt 模板](./safe-guides/Appendix/templates/rustfmt.toml.md)
- [clippy 模板](./safe-guides/Appendix/templates/clippy.toml.md)
- [deny 模板](./safe-guides/Appendix/templates/deny.toml.md)
- [E.工具链](./safe-guides/Appendix/tools/intro.md)
- [rustfmt](./safe-guides/Appendix/tools/rustfmt.md)
- [noisy-clippy](./safe-guides/Appendix/tools/noisy-clippy.md)
- [cargo-udeps](./safe-guides/Appendix/tools/cargo-udeps.md)
- [F.Cheat Sheet](./safe-guides/Appendix/cheat-sheet/README.md)
- [浮点数](./safe-guides/Appendix/cheat-sheet/Numbers/float.md)
- [G.优化指南](./safe-guides/Appendix/optimizing/intro.md)
- [H.编译参数说明](./safe-guides/Appendix/rustc-flag.md)
- [I.最佳实践](./safe-guides/Appendix/best-practice/intro.md)
- [初学者常见问题Q&A](./safe-guides/Appendix/best-practice/qa.md)
- [Rust 编程技巧](./safe-guides/Appendix/best-practice/tips.md)
- [J.贡献说明](./safe-guides/Appendix/contribution.md)
- [K.淘汰的规则](./safe-guides/Appendix/old_guidelines.md)
================================================
FILE: src/contribution.md
================================================
# 贡献者指南
目前项目处于初期,先以 issues 提建议为主,暂不支持 Pull Request。
等项目整体结构确定以后,再开始接受 Pull Request。
### 贡献名单
================================================
FILE: src/overview.md
================================================
# 1. 概述
## 状态
- 《Rust 编码规范》目前为 V 1.0 beta 试行版,改进内容参考 [Changelog](./Changelog.md)
## 详细
- [1.1 为什么需要 Rust 编码规范](./overview/why.md)
- [1.2 编码规范基本约定](./overview/convention.md)
## 介绍
Rust 语言社区内其实分散着很多编码规范,下面罗列一部分公开信息:
- [官方|Rust API 编写指南](https://rust-lang.github.io/api-guidelines/about.html)
- [官方 | Rust Style Guide](https://github.com/rust-dev-tools/fmt-rfcs/blob/master/guide/guide.md)
- [Rust's Unsafe Code Guidelines Reference](https://rust-lang.github.io/unsafe-code-guidelines/)
- [法国国家信息安全局 | Rust 安全(Security)规范](https://anssi-fr.github.io/rust-guide)
- [Apache Teaclave 安全计算平台 | Rust 开发规范](https://teaclave.apache.org/docs/rust-guildeline/)
- [PingCAP | 编码风格指南(包括 Rust 和 Go 等)](https://github.com/pingcap/style-guide)
- [Google Fuchsia 操作系统 Rust 开发指南](https://fuchsia.dev/fuchsia-src/development/languages/rust)
- [RustAnalyzer 编码风格指南](https://github.com/rust-analyzer/rust-analyzer/blob/master/docs/dev/style.md)
- [使用 Rust 设计优雅的 API](https://deterministic.space/elegant-apis-in-rust.html)
- [Rust FFI 指南](https://michael-f-bryan.github.io/rust-ffi-guide/)
上面这些除了 Rust 官方和法国国家信息安全局的编码规范之外,其他开源项目的编码规范主要是为了规范贡献者们遵循一个统一的编码风格。
所以,一个通用的,覆盖编码风格和具体编码实践的全面的编码规范,更有助于社区各个开源项目和各大公司参考去制定自己的编码规范。

本规范致力于成为统一的 Rust 编码规范,各大公司可以依赖本规范,结合自己的业务领域和团队习惯,形成自己的编码规范,并可以在日常实践中反哺本规范,让本规范更加完善。
================================================
FILE: src/safe-guides/Appendix/best-practice/intro.md
================================================
# I.最佳实践
## 列表
- [Q&A](./qa.md)
- [Tips](./tips.md)
================================================
FILE: src/safe-guides/Appendix/best-practice/qa.md
================================================
# 初学者常见问题Q&A
================================================
FILE: src/safe-guides/Appendix/best-practice/tips.md
================================================
# Rust 编程技巧
### 设计模式之构建者模式
当需要很多构造函数,或构造含有很多可选配置项时,宜使用构建者模式
**【描述】**
Rust 中没有默认的构造函数,都是自定义构造函数。
如果需要多个构造函数,或者构造时需要很多可选配置的复杂场景,那么构建者模式是适合你的选择。
**【示例】**
```rust
#[derive(Debug, PartialEq)]
pub struct Foo {
// Lots of complicated fields.
bar: String,
}
impl Foo {
// This method will help users to discover the builder
pub fn builder() -> FooBuilder {
FooBuilder::default()
}
}
#[derive(Default)]
pub struct FooBuilder {
// Probably lots of optional fields.
bar: String,
}
impl FooBuilder {
pub fn new(/* ... */) -> FooBuilder {
// Set the minimally required fields of Foo.
FooBuilder {
bar: String::from("X"),
}
}
pub fn name(mut self, bar: String) -> FooBuilder {
// Set the name on the builder itself, and return the builder by value.
self.bar = bar;
self
}
// If we can get away with not consuming the Builder here, that is an
// advantage. It means we can use the FooBuilder as a template for constructing
// many Foos.
pub fn build(self) -> Foo {
// Create a Foo from the FooBuilder, applying all settings in FooBuilder
// to Foo.
Foo { bar: self.bar }
}
}
#[test]
fn builder_test() {
let foo = Foo {
bar: String::from("Y"),
};
let foo_from_builder: Foo = FooBuilder::new().name(String::from("Y")).build();
assert_eq!(foo, foo_from_builder);
}
```
## 善用标准库中提供的迭代器适配器方法来满足自己的需求
**【描述】**
Rust 标准库中提供了很多迭代器方法,要学会使用它们,选择合适的方法来满足自己的需求。
下面示例中,反例中的迭代器适配器方法,都可以用对应的正例中的方法代替。
**【反例】**
```rust
// explicit_counter_loop
let v = vec![1];
fn bar(bar: usize, baz: usize) {}
let mut i = 0;
for item in &v {
bar(i, *item);
i += 1;
}
// filter_map_identity
let iter = vec![Some(1)].into_iter();
iter.filter_map(|x| x);
// filter_next
let vec = vec![1];
vec.iter().filter(|x| **x == 0).next();
// flat_map_identity
let iter = vec![vec![0]].into_iter();
iter.flat_map(|x| x);
// flat_map_option
let nums: Vec<i32> = ["1", "2", "whee!"].iter().flat_map(|x| x.parse().ok()).collect();
```
**【正例】**
```rust
// explicit_counter_loop
let v = vec![1];
fn bar(bar: usize, baz: usize) {}
for (i, item) in v.iter().enumerate() { bar(i, *item); }
// filter_map_identity
let iter = vec![Some(1)].into_iter();
iter.flatten();
// filter_next
let vec = vec![1];
vec.iter().find(|x| **x == 0);
// flat_map_identity
let iter = vec![vec![0]].into_iter();
iter.flatten();
// flat_map_option
let nums: Vec<i32> = ["1", "2", "whee!"].iter().filter_map(|x| x.parse().ok()).collect();
```
**【Lint 检测】**
| lint name | Clippy 可检测 | Rustc 可检测 | Lint Group | level |
| ------------------------------------------------------------ | ------------- | ------------ | ---------- | ----- |
| [explicit_counter_loop](https://rust-lang.github.io/rust-clippy/master/#explicit_counter_loop) | yes | no | complexity | warn |
| [filter_map_identity](https://rust-lang.github.io/rust-clippy/master/#filter_map_identity) | yes | no | complexity | warn |
| [filter_next](https://rust-lang.github.io/rust-clippy/master/#filter_next) | yes | no | complexity | warn |
| [flat_map_identity](https://rust-lang.github.io/rust-clippy/master/#flat_map_identity) | yes | no | complexity | warn |
| [flat_map_option](https://rust-lang.github.io/rust-clippy/master/#flat_map_option) | yes | no | pedantic | allow |
## 可以使用`Cow<str>`来代替直接使用字符串,它可以减少Copy
**【描述】**
使用 `Cow<str>` 作为字符串处理函数参数和返回值,可以尽可能地减少数据Copy 和 内存分配。当字符串没有修改的时候,实际使用的是 `&'a str`,只有当数据修改的时候才会使用`String`。对于读操作大于写操作的场景,使用 `Cow<str>` 比较合适。
**【示例】**
```rust
// 对输入的字符串进行转义
pub fn naive<'a, S: Into<Cow<'a, str>>>(input: S) -> Cow<'a, str> {
let input = input.into();
fn is_trouble(c: char) -> bool {
c == '<' || c == '>' || c == '&'
}
if input.contains(is_trouble) {
let mut output = String::with_capacity(input.len());
for c in input.chars() {
match c {
'<' => output.push_str("<"),
'>' => output.push_str(">"),
'&' => output.push_str("&"),
_ => output.push(c)
}
}
// 只有在字符串修改的时候才使用 String
Cow::Owned(output)
} else {
//其他情况使用 &str
input
}
}
```
## 错误处理:根据应用还是库来选择不同的错误处理方式
**【描述】**
如果编写应用,建议使用` Error` trait对象;如果编写库,则建议返回自定义错误类型,方便下游处理
**【正例】**
```rust
// 对于应用使用 Error trait 对象更加方便
pub fn print(&self, languages: &Languages) -> Result<String, Box<dyn Error>> {
// do something
}
// 对于库,暴露自定义错误类型更加方便下游处理错误
#[derive(Debug)]
pub struct SendError<T>(pub T);
impl<T> fmt::Display for SendError<T> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(fmt, "channel closed")
}
}
```
## 嵌入式(no-std): 将一些公用的类型、函数、宏等集中到一个自定义的 `baremetal-std`
**【描述】**
虽然 `no-std` 下不能用Rust 的标准库,但是可以自定义 `no-std` 下的标准库 `baremetal-std`,用于积累 `no-std` 下常用的公共库。
================================================
FILE: src/safe-guides/Appendix/cheat-sheet/Numbers/float.md
================================================
# Float Cheat Sheet
> From: [https://github.com/brazzy/floating-point-gui.de](https://github.com/brazzy/floating-point-gui.de)
## 浮点数类型
Rust 有 [IEEE 754](https://floating-point-gui.de/formats/fp/) 单精度 (32-bit) 和 双精度 (64-bit) 类型:
```rust
let x: f32 = 0.1; // 32-bit float
let y: f64 = 0.1; // 64-bit float
```
默认的浮点数类型是 `f64`:
```rust
let z = 0.1; // 64-bit float
```
## Decimal Types
Rust 没有内建的 Decimal 类型,但是有第三方库 [rust_decimal](https://crates.io/crates/rust-decimal) 来支持 Decimal 类型。该库实现了 128-bit [有限精度的(limited-precision)](https://floating-point-gui.de/formats/exact/) 关键字 `Decimal` 表示 Decimal 类型:
```rust
use rust_decimal::prelude::*;
let a = Decimal::new(1, 1); // second param is the number of fractional digits
let b = Decimal::new(2, 1); // a Decimal representing exactly 0.2
let c = a + b; // a Decimal representing exactly 0.3
```
## 如何四舍五入
生成字符串:
```rust
format!("{:.2}", 1.2399); // returns "1.24"
format!("{:.3}", 1.2399); // returns "1.240"
format!("{:.2}", 1.2); // returns "1.20"
```
打印标准输出:
```rust
println!("{:.2}", 1.2399); // prints "1.24"
```
这个 `round` 方法返回与数字最接近的整数。它使用 [四舍五入模式(rounding mode)](https://floating-point-gui.de/errors/rounding/) ,"从零开始四舍五入",并且对`f32`和`f64`类型都有效。
```rust
let f: f64 = 3.3;
let g: f64 = -3.3;
f.round(); // returns 3.0
g.round(); // returns -3.0
```
`rust_decimal` crate 包含`round_dp`方法,它使用Banker的[舍入模式](https://floating-point-gui.de/errors/rounding/)。
```rust
let pi = Decimal::from_str("3.1415926535897932384626433832").unwrap();
println!("{}", pi.round_dp(2).to_string()); // prints "3.14"
```
`rust_decimal` crate 还包含`round_dp_with_strategy`方法,允许你指定一个四舍五入策略。
```rust
let i = Decimal::from_str("1.25").unwrap();
println!(
"{}",
i.round_dp_with_strategy(1, RoundingStrategy::RoundDown)
.to_string()
) // prints "1.2"
```
## Resources
- [rust_decimal crate](https://crates.io/crates/rust-decimal)
================================================
FILE: src/safe-guides/Appendix/cheat-sheet/README.md
================================================
# F.Cheat Sheet
这里用于梳理 Rust 相关的 Cheat Sheet。
- [数字]()
- [浮点数](./safe-guides/Appendix/cheat-sheet/Numbers/float.md)
## 资源
[https://cheats.rs/](https://cheats.rs/)
================================================
FILE: src/safe-guides/Appendix/contribution.md
================================================
# J.贡献说明
欢迎直接提交 Issues 或 PR 直接参与评审和完善(包括精简、增补新的规则)编码规范。
================================================
FILE: src/safe-guides/Appendix/dev_env.md
================================================
# A.开发环境
## 编辑器推荐
VSCode + Rust Analyzer 扩展
其他辅助vscode 扩展:
[flowistry](https://github.com/willcrichton/flowistry) ,可以帮助开发者理解 Rust 程序。
## IDE 推荐
Clion
## 工具链安装
使用[ `Rustup`](https://github.com/rust-lang/rustup)。 如需替代安装方式,为了保证安全,最好选择官方推荐的替代安装方式。
## **Rust 版次(Edition)** **说明**
Rust从2015开始,每三年发布一个 Edition 版次:
> 1. Rust 2015 edition (Rust 1.0.0 + )
> 2. Rust 2018 edition (Rust 1.31.0 +)
> 3. Rust 2021 edition (Rust 1.56.0 +)
以此类推。Edition是向前兼容的。Edition 和语义化版本是正交的,不冲突。
关于 Edition 更详细的内容可以查看:[https://doc.rust-lang.org/edition-guide/](https://doc.rust-lang.org/edition-guide/)
## **稳定版、 开发版和测试版工具链**
Rust 工具链提供三种不同的发布渠道:
> 1. Nightly(开发版),每晚发布(release)一次。
> 2. Beta(测试版),每六周发布一次,基于Nightly版本。
> 3. Stable(稳定版),每六周发布一次,基于 beta版本。
注意:
> 1. 推荐使用 Stable Rust。
> 2. 在基于Nightly Rust 开发项目的时候,最好通过在项目中增加 rust-toolchain 文件来指定一个固定的版本,避免因为Nightly Rust 的频繁变更而导致项目编译问题。
> 3. 当在稳定版工作的时候,如果需要Nightly工具链,不需要整体上去切换工具链到Nightly,只需要再命令中指明Nightly就可以了。比如 `cargo +nightly fmt`。
## **包管理器 Cargo**
Cargo 是 Rust 项目必不可少的包管理器,除此之外,它也是一种工作流:
> 1. 可以用Cargo创建一个项目(bin/lib)
> 2. 可以用它编译项目
> 3. 可以用它生产项目的文档(依据文档注释)
> 4. 可以用它运行单元测试(test)和基准测试(bench)
> 5. 可以用它下载和管理crate依赖
> 6. 可以用它分发软件包,默认分发到 [crates.io](http://crates.io/) 上面
> 7. 可以为它编写插件,使用子命令的方式,扩展它的功能。
Cargo 通过 Cargo.toml 配置文件来管理 crate。
Toml 配置文件是一种最小化且无歧义的文件格式,Rust社区最常用Toml。可以通过 [toml.io](http://toml.io/) 进一步了解 Toml 的细节。
值得说明的是,在配置文件中如果有 [profile.*] 这种配置,需要引起注意,因为这类配置决定了编译器的调用方式,比如:
> 1. debug-assertions ,决定了是否开启debug断言。
> 2. overflow-checks,决定了是否检查整数运算溢出。
关于Cargo的更多细节可以查看:[https://doc.rust-lang.org/cargo/index.html](https://doc.rust-lang.org/cargo/index.html)
## 常用Cargo插件
**Clippy**
Clippy 是一个静态分析工具,它提供了很多检查,比如错误、 样式、 性能问题、 Unsafe UB问题等等。从1.29版本开始,Clippy可以用于 Stable Rust中。
可以通过 `rustup component add clippy` 来安装此 Cargo 插件。
细节参考:[https://github.com/rust-lang/rust-clippy](https://github.com/rust-lang/rust-clippy)
Clippy 的全部 lint 检查建议列表: [https://rust-lang.github.io/rust-clippy/master/](https://rust-lang.github.io/rust-clippy/master/)
**Rustfmt**
Rustfmt 是一个根据风格指南原则来格式化代码的工具。
可以通过 Rustup 来安装它: `rustup component add rustfmt`
Rustfmt 依赖的社区维护的 Rust风格指南:[https://github.com/rust-dev-tools/fmt-rfcs/tree/master/guide](https://github.com/rust-dev-tools/fmt-rfcs/tree/master/guide)
开发者也可以通过 `rustfmt.toml` 或 `.rustfmt.toml` 来定制团队统一的代码风格,比如:
```toml
# Set the maximum line width to 120
max_width = 120
# Maximum line length for single line if-else expressions
single_line_if_else_max_width = 40
```
**Rustfix**
从 Rust 2018 edition开始,Rustfix就被包含在 Rust 中。它可以用来修复编译器警告。
需要注意的是,在使用 cargo fix 进行自动修复警告的时候,需要开发者确认这个警告是否真的需要修复,并且要验证修复的是否正确。
**Cargo Edit**
Cargo Edit插件为Cargo扩展了三个命令:
> 1. Cargo add,在命令行增加新的依赖,而不需要去知道这个依赖的语义版本。
> 2. Cargo rm,在命令行删除一个指定依赖。
> 3. Cargo upgrade,在命令行升级一个指定依赖。
Cargo-edit地址:[https://github.com/killercup/cargo-edit](https://github.com/killercup/cargo-edit)
**Cargo Audit**
Cargo Audit 可以根据 Rust安全警报数据库(RestSec Advisory Database )的漏洞数据,扫描crate以及它的所有依赖库,然后给出一份安全报告。
更多细节:[https://github.com/RustSec/cargo-audit](https://github.com/RustSec/cargo-audit)
Rust 安全警报数据库:[https://rustsec.org/](https://rustsec.org/)
**Cargo Outdated**
该插件可以检测依赖库是否有新版本可用。
更多细节:[https://github.com/kbknapp/cargo-outdated](https://github.com/kbknapp/cargo-outdated)
**Cargo Deny**
该插件可以检测依赖中的软件许可证(License),如果和开发者配置的不符合,则会拒绝使用该依赖。
更多细节:[https://github.com/EmbarkStudios/cargo-deny](https://github.com/EmbarkStudios/cargo-deny)
Cargo Deny Book: [https://embarkstudios.github.io/cargo-deny/](https://embarkstudios.github.io/cargo-deny/)
## Rustup 和 crates 国内镜像
### 加速 Rustup
我们需要指定 `RUSTUP_DIST_SERVER`(默认指向 https://static.rust-lang.org)和 `RUSTUP_UPDATE_ROOT` (默认指向https://static.rust-lang.org/rustup),这两个网站均在中国大陆境外,因此在中国大陆访问会很慢,需要配置成境内的镜像。
以下` RUSTUP_DIST_SERVER` 和 `RUSTUP_UPDATE_ROOT` 可以组合使用。
```toml
# 清华大学
RUSTUP_DIST_SERVER=https://mirrors.tuna.tsinghua.edu.cn/rustup
# 中国科学技术大学
RUSTUP_DIST_SERVER=https://mirrors.ustc.edu.cn/rust-static
RUSTUP_UPDATE_ROOT=https://mirrors.ustc.edu.cn/rust-static/rustup
# 上海交通大学
RUSTUP_DIST_SERVER=https://mirrors.sjtug.sjtu.edu.cn/rust-static/
# 字节跳动
RUSTUP_DIST_SERVER="https://rsproxy.cn"
RUSTUP_UPDATE_ROOT="https://rsproxy.cn/rustup"
```
### 加速 crates
将如下配置写入 `$HOME/.cargo/config` 文件:
```toml
# 放到 `$HOME/.cargo/config` 文件中
[source.crates-io]
registry = "https://github.com/rust-lang/crates.io-index"
# 替换成你偏好的镜像源,比如 字节跳动的
replace-with = 'rsproxy'
# 清华大学
[source.tuna]
registry = "https://mirrors.tuna.tsinghua.edu.cn/git/crates.io-index.git"
# 中国科学技术大学
[source.ustc]
registry = "git://mirrors.ustc.edu.cn/crates.io-index"
# 上海交通大学
[source.sjtu]
registry = "https://mirrors.sjtug.sjtu.edu.cn/git/crates.io-index"
# rustcc社区
[source.rustcc]
registry = "git://crates.rustcc.cn/crates.io-index"
# 字节跳动 https://rsproxy.cn/
[source.rsproxy]
registry = "https://rsproxy.cn/crates.io-index"
[registries.rsproxy]
index = "https://rsproxy.cn/crates.io-index"
[net]
git-fetch-with-cli = true
```
### 安装 Rust
使用 字节跳动源:
```rust
# export the env above first
curl --proto '=https' --tlsv1.2 -sSf https://rsproxy.cn/rustup-init.sh | sh
```
================================================
FILE: src/safe-guides/Appendix/old_guidelines.md
================================================
# K.淘汰的规则
---
## 常量
### G.CNS.03 不宜将量大的数据结构定义为常量
**淘汰原因**
虽然常量本质上是会内联,但Rust 支持类似于复制消除(Copy Elision)的优化(非强制),而且在不断改进中,对于这种大的数据应该会有相关优化。这里建议用静态变量来代替常量,也许会引入使用的复杂性,所以这条规则被淘汰。只保留对固定长度数组相关的规则。
相应修改:[G.TYP.ARR.01] 的描述也有相对修改。
**【级别】** 建议
**【描述】**
因为[常量会到处内联](https://doc.rust-lang.org/reference/items/constant-items.html#constant-items),即复制到各个使用到它的地方。而静态变量不会内联,它是全局的且有一个引用地址。
所以当要创建一个很大的常量数组时,应该考虑将其换成静态变量以提高程序运行效率。(详情可见:[const-vs-static](https://rust-lang.github.io/rfcs/0246-const-vs-static.html#motivation))
相关:[G.TYP.Array.01 ]
**【反例】**
```rust
fn main() {
const MONTHS: [&str; 12] = ["January", "Feburary", "March", "April",
"May", "June", "July", "August",
"September", "October", "November", "December"];
}
```
**【正例】**
```rust
fn main() {
static MONTHS: [&str; 12] = ["January", "Feburary", "March", "April",
"May", "June", "July", "August",
"September", "October", "November", "December"];
}
```
**【Lint 检测】**
| lint name | Clippy 可检测 | Rustc 可检测 | Lint Group | 是否可定制 |
| ------------------------------------------------------------ | ------------- | ------------ | ---------- | ----- |
| _ | no | no | _ | yes |
**【定制化参考】**
这条规则如果需要定制Lint,则需要找出每个定义的常量再判断其空间占用,或可直接排除基础类型以外的数据类型。
## 变量
### P.VAR.02 禁止将局部变量的引用返回函数外
**淘汰原因**
Rust 编译器可以检测到这种情况,之前考虑到编译器错误比较晦涩,列出该规则,但是进一步考虑到这个应该是 Rust 开发者最基本的认知,顾淘汰此规则。
**【描述】**
局部变量生命周期始于其声明终于其作用域结束。如果在其生命周期之外被引用,则程序的行为是未定义的。当然,Rust 编译器也会阻止你这么干。
*注: Rust 编译器可以检测到这种情况,但是编译器错误比较晦涩,本原则用来提示开发者注意这种情况。 *
**【反例】**
```rust
fn makestr() -> &String {
let a = "test".to_string();
&a
}
pub fn main() {
let a = makestr();
}
```
### G.VAR.01 交换两个变量的值应使用 `swap` 而非赋值
**淘汰原因**
该条规则属于教程向,这应该是 Rust 开发者的基本认知。也不应该引导开发者使用swap。
**【级别】** 建议
**【描述】**
对于包含 `swap` 方法的类型,如 `ptr`、`slice`、`Cell`、`RefCell`、`VecDeque` 等建议使用该类型的 `swap` 方法进行交换。
对其他类型可以使用函数 `std::mem::swap` 进行变量值的交换。
**【反例】**
```rust
let mut a = 1;
let mut b = 2;
let mut c = 0; // 辅助交换的变量
c = a;
a = b;
b = c;
```
**【正例】**
```rust
let mut a = 1;
let mut b = 2;
std::mem::swap(&mut a, &mut b);
```
**【Lint 检测】**
| lint name | Clippy 可检测 | Rustc 可检测 | Lint Group | 是否可定制 |
| ------------------------------------------------------------ | ------------- | ------------ | ---------- | ----- |
| _ | no | no | _ | yes |
**【定制化参考】**
这条规则如果需要定制Lint,则可以检测变量赋值操作,识别交换语义,推荐用户使用 `swap` 函数。
## 数据类型
### P.TYP.01 类型转换要尽量使用安全的方式
**淘汰原因**
和 G.TYP.01 有点重复。
**【描述】**
Rust 中的类型转换有多种方式,其中 `as` 强转、Unsafe 的 `std::mem::transmute` 为不安全转换。`From/Into`安全转换函数为安全转换。在使用类型转换时,应优先使用安全转换方式。
**【反例】**
```rust
// 产生数据截断
let a = i32::MAX;
let b = a as u16;
println!("{}, {}", a, b); // 2147483647, 65535
// 产生精度损失
let a = std::f64::consts::PI;
let b = a as f32;
println!("{}, {}", a, b); // 3.141592653589793, 3.1415927
// 结果不正确
let a: f64 = 123456.0;
let b: i64 = unsafe {
std::mem::transmute(a)
};
println!("{}, {}", a, b); // 123456, 4683220244930494464
```
**【正例】**
```rust
let a: f32 = 123456.0;
let b: f64 = a.try_into().expect("trans failed");
println!("{}, {}", a, b); // 123456, 123456
```
### P.TYP.02 对数组和集合容器进行索引要使用 `usize` 类型
**淘汰原因**
这属于 Rust 开发者必备基本认知,有点偏教程向,故淘汰。
**【描述】**
Rust 中只允许索引为 `usize` 类型,因为:
1. 负索引是无意义的。
2. `usize`和 裸指针大小相同,意味着指针算法不会有任何隐藏的强制转换
3. `std::mem::size_of()` 和 `std::mem::align_of()` 的函数返回 `usize` 类型。
4. `usize` 不会因为平台架构的切换而导致索引值被截断的问题,比如 将`u32`类型的索引 用到 16位大小的嵌入式平台就会出问题。
### G.TYP.UNT.01 当函数不关心返回值但要处理错误时应使用单元类型
**淘汰原因**
这属于 Rust 开发者必备基本认知,有点偏教程向,故淘汰。
**【级别】** 建议
【定制化参考】
可以检测使用 `Option<T>` 包含 `Error` 类型的情况,发出警告。
**【描述】**
单元类型代表 `无返回值`。当返回类型无返回值但要处理错误时,应使用Result<(), Error>类型,
而非Option类型。
**【反例】**
```rust
fn f() -> Option<Error> {
// ...
None
}
```
**【正例】**
```rust
// 表示该函数要么成功,要么返回各自错误
fn f() -> Result<(), Error> {
// ...
// Error handle
Ok(())
}
```
**【Lint 检测】**
| lint name | Clippy 可检测 | Rustc 可检测 | Lint Group | 是否可定制 |
| ------------------------------------------------------------ | ------------- | ------------ | ---------- | ----- |
| _ | no | no | _ | yes |
### G.TYP.INT.02 对于大整数字面量宜使用十六进制表示
**淘汰原因**
这个不做限制了,因人而异。
**【级别】** 建议
**【描述】**
略
**【反例】**
```rust
let a = `255`
let b = `65_535`
let c =`4_042_322_160`
```
**【正例】**
```rust
let a = `0xFF`
let b = `0xFFFF`
let c = `0xF0F0_F0F0
```
**【Lint 检测】**
| lint name | Clippy 可检测 | Rustc 可检测 | Lint Group | level |
| ------------------------------------------------------------ | ------------- | ------------ | ----------- | ----- |
| [decimal_literal_representation](https://rust-lang.github.io/rust-clippy/master/#decimal_literal_representation) | yes | no | restriction | allow |
### P.TYP.REF.01 使用引用的时候要注意其生命周期不要重合
**淘汰原因**
偏教程向,这应该是 Rust 开发者基本认知,而且编译器会报错。
**【描述】**
在使用 引用的时候,要注意分析其生命周期,不可变借用和可变借用之间,以及可变借用之间不要有重叠。
**【反例】**
```rust
fn main(){
let mut s = String::from("hello");
// r1 是不可变借用,其生命周期和 可变借用 r3 重叠,所以会出问题
let r1 = &s; // no problem ---------------- lifetime r1 start
let r2 = &mut s; // no problem
let r3 = &mut s; // BIG PROBLEM -------------- lifetime r3 start
println!("{}, {}, and {}", r1, r2, r3); // lifetime r1, r2, r3 end;
}
```
**【正例】**
```rust
fn main(){
let mut s = String::from("hello");
let r1 = &s; // no problem
let r2 = &mut s; // no problem
let r3 = &mut s; // no PROBLEM
// println!("{}, {}, and {}", r1, r2, r3);
}
```
### P.TYP.TUP.01 宜使用元组解构来同时定义多个变量
**淘汰原因**
偏教程向,这应该是 Rust 开发者基本认知。
**【描述】**
可以利用元组解构的特性,来更好地精简代码。
**【示例】**
```rust
struct A(i32, i32);
fn hello( A(a, b): A){
println!("{}, {}", a, b);
}
fn main(){
let a = A(1, 2) ;
hello(a);
}
```
### P.TYP.ARR.01 当数组长度在编译期就已经确定,应优先使用固定长度数组,而非动态数组( `Vec<T>`)
**淘汰原因**
偏教程向,这应该是 Rust 开发者基本认知。
**【描述】**
固定长度数组会根据元素类型,优先选择存储在栈上,可以优化内存分配。当然,过大的数组可以酌情考虑放到堆内存,这个依据具体场景来决定。
当编译期长度可以确定,但长度并不是唯一确定的,那么可以考虑使用常量泛型。注意:常量泛型特性从 Rust 1.51版稳定。
**【示例】**
```rust
pub struct Grid {
array: [u32; 5],
width: usize,
height: usize,
}
```
常量泛型:
```rust
pub struct Grid<T, const W: usize, const H: usize>
where
{
array: [[T; W]; H],
}
impl<T, const W: usize, const H: usize> Default for Grid<T, W, H>
where
T: Default + Copy,
{
fn default() -> Self {
Self {
array: [[T::default(); W ]; H],
}
}
}
const WIDTH: usize = 300;
const HEIGHT: usize = 200;
fn main(){
let _g = Grid::<usize, 3, 4>::default();
let _h = Grid::<usize, WIDTH, HEIGHT>::default();
}
```
注意,常量泛型目前还有一些特性并未完善,比如下面示例中的 `#![feature(generic_const_exprs)]` 特性,需要在 Nightly Rust 下使用。
```rust
#![feature(generic_const_exprs)]
pub struct Grid<T, const W: usize, const H: usize>
where
[(); W * H]: Sized,
{
array: [T; W * H],
}
impl<T, const W: usize, const H: usize> Default for Grid<T, W, H>
where
[(); W * H]: Sized,
T: Default + Copy,
{
fn default() -> Self {
Self {
array: [T::default(); W * H],
}
}
}
const WIDTH: usize = 300;
const HEIGHT: usize = 200;
fn main(){
let _g = Grid::<usize, 3, 4>::default();
let _h = Grid::<usize, WIDTH, HEIGHT>::default();
}
```
### P.TYP.SCT.02 当需要很多构造函数,或构造含有很多可选配置项时,宜使用构建者模式
**淘汰原因**
这条属于编程最佳实践,放到规范中有点臃肿,独立到最佳实践中。
**【描述】**
Rust 中没有默认的构造函数,都是自定义构造函数。
如果需要多个构造函数,或者构造时需要很多可选配置的复杂场景,那么构建者模式是适合你的选择。
**【示例】**
```rust
#[derive(Debug, PartialEq)]
pub struct Foo {
// Lots of complicated fields.
bar: String,
}
impl Foo {
// This method will help users to discover the builder
pub fn builder() -> FooBuilder {
FooBuilder::default()
}
}
#[derive(Default)]
pub struct FooBuilder {
// Probably lots of optional fields.
bar: String,
}
impl FooBuilder {
pub fn new(/* ... */) -> FooBuilder {
// Set the minimally required fields of Foo.
FooBuilder {
bar: String::from("X"),
}
}
pub fn name(mut self, bar: String) -> FooBuilder {
// Set the name on the builder itself, and return the builder by value.
self.bar = bar;
self
}
// If we can get away with not consuming the Builder here, that is an
// advantage. It means we can use the FooBuilder as a template for constructing
// many Foos.
pub fn build(self) -> Foo {
// Create a Foo from the FooBuilder, applying all settings in FooBuilder
// to Foo.
Foo { bar: self.bar }
}
}
#[test]
fn builder_test() {
let foo = Foo {
bar: String::from("Y"),
};
let foo_from_builder: Foo = FooBuilder::new().name(String::from("Y")).build();
assert_eq!(foo, foo_from_builder);
}
```
### P.TYP.ENM.01 需要取出 Enum 值的时候宜使用 `std::mem::take/swap/replace`
**淘汰原因**
偏教程向。
**【描述】**
需要取出 Enum 中值的时候,可能会遇到所有权的限制,此时可以使用 `std::men::take` 获取当前的值,而将默认值替换原值,这样可以避免所有权的限制。
但是 `std::men::take` 只适合实现 `Default` 的类型,这样就有默认实现可以替换了。
如果没有实现 `Default` 的类型,可以使用 `std::men::swap` 或 `std::mem::replace` 用给定的值来替换原值。
**【正例】**
```rust
use std::mem;
enum MultiVariateEnum {
A { name: String },
B { name: String },
C,
D
}
fn swizzle(e: &mut MultiVariateEnum) {
use MultiVariateEnum::*;
*e = match e {
// Ownership rules do not allow taking `name` by value, but we cannot
// take the value out of a mutable reference, unless we replace it:
A { name } => B { name: mem::take(name) },
B { name } => A { name: mem::take(name) },
C => D,
D => C
}
}
```
## 表达式
### G.EXP.04 不应使用无效表达式语句
**淘汰原因**
属于代码逻辑问题,不应放到规范中。这类问题交给 Clippy 这类工具即可。
**【级别】** 建议
**【描述】**
无效的表达式语句,虽然会执行,但实际并没有起到什么效果。
也有例外情况存在。
**【反例】**
```rust
a+1;
```
**【正例】**
```rust
let a = 41;
let a = a+1;
```
**【例外】**
像在下面代码中,为了确保常量函数 `new` 可以在输入参数超出 MAX 限制的情况下 panic,使用了一个数组的技巧:` ["tag number out of range"][(byte > Self::MAX) as usize];` 。因为目前 在常量上下文中还无法直接使用 `panic!`,等 `const_panic` 功能稳定就可以了。
如果不加 `#[allow(clippy::no_effect)]` ,Clippy 会有警告。
```rust
// From: https://docs.rs/crate/der/0.4.1/source/src/tag/number.rs
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub struct TagNumber(pub(super) u8);
impl TagNumber {
/// Maximum tag number supported (inclusive).
pub const MAX: u8 = 30;
/// Create a new tag number (const-friendly).
///
/// Panics if the tag number is greater than [`TagNumber::MAX`]. For a fallible
/// conversion, use [`TryFrom`] instead.
#[allow(clippy::no_effect)]
pub const fn new(byte: u8) -> Self {
// TODO(tarcieri): hax! use const panic when available
["tag number out of range"][(byte > Self::MAX) as usize];
Self(byte)
}
// ...
}
```
**【Lint 检测】**
| lint name | Clippy 可检测 | Rustc 可检测 | Lint Group | level |
| ------------------------------------------------------------ | ------------- | ------------ | -------------- | ----- |
| [no_effect](https://rust-lang.github.io/rust-clippy/master/#no_effect) | yes | no | **complexity** | warn |
## 控制流程
### G.CTF.01 避免在流程控制分支中使用重复代码
**淘汰原因**
属于代码逻辑问题,不应放到规范中。这类问题交给 Clippy 这类工具即可。
**【级别】** 建议
**【描述】**
略
**【反例】**
```rust
let foo = if … {
println!("Hello World");
13
} else {
println!("Hello World");
42
};
```
**【正例】**
```rust
println!("Hello World");
let foo = if … {
13
} else {
42
};
```
**【Lint 检测】**
| lint name | Clippy 可检测 | Rustc 可检测 | Lint Group | level |
| ------ | ---- | --------- | ------ | ------ |
| [branches_sharing_code](https://rust-lang.github.io/rust-clippy/master/#branches_sharing_code) | yes| no | nursery | allow |
### G.CTF.02 控制流程的分支逻辑要保持精炼
**淘汰原因**
属于代码逻辑问题,不应放到规范中。这类问题交给 Clippy 这类工具即可。
**【级别】** 建议
**【描述】**
略
**【反例】**
```rust
if x {
…
} else { // collapsible_else_if
if y {
…
}
}
if x { // collapsible_if
if y {
…
}
}
// collapsible_match
fn func(opt: Option<Result<u64, String>>) {
let n = match opt {
Some(n) => match n {
Ok(n) => n,
_ => return,
}
None => return,
};
}
// double_comparisons
# let x = 1;
# let y = 2;
if x == y || x < y {}
// wildcard_in_or_patterns
match "foo" {
"a" => {},
"bar" | _ => {},
}
```
**【正例】**
```rust
// else if
if x {
…
} else if y {
…
}
// Merge multiple conditions
if x && y {
…
}
// match
fn func(opt: Option<Result<u64, String>>) {
let n = match opt {
Some(Ok(n)) => n,
_ => return,
};
}
// comparisons
# let x = 1;
# let y = 2;
if x <= y {}
// wildcard_in_or_patterns
match "foo" {
"a" => {},
_ => {},
}
```
**【Lint 检测】**
| lint name | Clippy 可检测 | Rustc 可检测 | Lint Group | level |
| ------------------------------------------------------------ | ------------- | ------------ | -------------- | ----- |
| [collapsible_else_if](https://rust-lang.github.io/rust-clippy/master/#collapsible_else_if) | yes | no | style | warn |
| [collapsible_if](https://rust-lang.github.io/rust-clippy/master/#collapsible_if) | yes | no | style | warn |
| [collapsible_match](https://rust-lang.github.io/rust-clippy/master/#collapsible_match) | yes | no | style | warn |
| [double_comparisons](https://rust-lang.github.io/rust-clippy/master/#double_comparisons) | yes | no | **complexity** | warn |
| [wildcard_in_or_patterns](https://rust-lang.github.io/rust-clippy/master/#wildcard_in_or_patterns) | yes | no | **complexity** | warn |
### G.CTF.06 善用标准库中提供的迭代器适配器方法来满足自己的需求
**淘汰原因**
属于最佳实践编程技巧类。
**【级别】** 建议
**【描述】**
Rust 标准库中提供了很多迭代器方法,要学会使用它们,选择合适的方法来满足自己的需求。
下面示例中,反例中的迭代器适配器方法,都可以用对应的正例中的方法代替。
**【反例】**
```rust
// explicit_counter_loop
let v = vec![1];
fn bar(bar: usize, baz: usize) {}
let mut i = 0;
for item in &v {
bar(i, *item);
i += 1;
}
// filter_map_identity
let iter = vec![Some(1)].into_iter();
iter.filter_map(|x| x);
// filter_next
let vec = vec![1];
vec.iter().filter(|x| **x == 0).next();
// flat_map_identity
let iter = vec![vec![0]].into_iter();
iter.flat_map(|x| x);
// flat_map_option
let nums: Vec<i32> = ["1", "2", "whee!"].iter().flat_map(|x| x.parse().ok()).collect();
```
**【正例】**
```rust
// explicit_counter_loop
let v = vec![1];
fn bar(bar: usize, baz: usize) {}
for (i, item) in v.iter().enumerate() { bar(i, *item); }
// filter_map_identity
let iter = vec![Some(1)].into_iter();
iter.flatten();
// filter_next
let vec = vec![1];
vec.iter().find(|x| **x == 0);
// flat_map_identity
let iter = vec![vec![0]].into_iter();
iter.flatten();
// flat_map_option
let nums: Vec<i32> = ["1", "2", "whee!"].iter().filter_map(|x| x.parse().ok()).collect();
```
**【Lint 检测】**
| lint name | Clippy 可检测 | Rustc 可检测 | Lint Group | level |
| ------------------------------------------------------------ | ------------- | ------------ | ---------- | ----- |
| [explicit_counter_loop](https://rust-lang.github.io/rust-clippy/master/#explicit_counter_loop) | yes | no | complexity | warn |
| [filter_map_identity](https://rust-lang.github.io/rust-clippy/master/#filter_map_identity) | yes | no | complexity | warn |
| [filter_next](https://rust-lang.github.io/rust-clippy/master/#filter_next) | yes | no | complexity | warn |
| [flat_map_identity](https://rust-lang.github.io/rust-clippy/master/#flat_map_identity) | yes | no | complexity | warn |
| [flat_map_option](https://rust-lang.github.io/rust-clippy/master/#flat_map_option) | yes | no | pedantic | allow |
## 字符串
### P.STR.03 可以使用`Cow<str>`来代替直接使用字符串,它可以减少Copy
**淘汰原因**
最佳实践编程技巧类。
**【描述】**
使用 `Cow<str>` 作为字符串处理函数参数和返回值,可以尽可能地减少数据Copy 和 内存分配。当字符串没有修改的时候,实际使用的是 `&'a str`,只有当数据修改的时候才会使用`String`。对于读操作大于写操作的场景,使用 `Cow<str>` 比较合适。
**【示例】**
```rust
// 对输入的字符串进行转义
pub fn naive<'a, S: Into<Cow<'a, str>>>(input: S) -> Cow<'a, str> {
let input = input.into();
fn is_trouble(c: char) -> bool {
c == '<' || c == '>' || c == '&'
}
if input.contains(is_trouble) {
let mut output = String::with_capacity(input.len());
for c in input.chars() {
match c {
'<' => output.push_str("<"),
'>' => output.push_str(">"),
'&' => output.push_str("&"),
_ => output.push(c)
}
}
// 只有在字符串修改的时候才使用 String
Cow::Owned(output)
} else {
//其他情况使用 &str
input
}
}
```
## 集合容器
### P.CLT.01 根据集合各自的特点选择合适的集合类型
**淘汰原因**
文档教程向,不该放到规范中。
**【描述】**
Rust 标准库内置的集合类型,在安全和性能方面还是比较靠谱的。需要仔细阅读标准库中各类集合类型的优缺点来选择合适的类型。
**下列场景考虑 `Vec`**
- 你想要一个可动态增长大小(堆分配)的数组
- 你想要一个栈结构
- 你想要集合元素按特定顺序排序,并且仅需要在结尾追加新元素
- 你可能只是想临时收集一些元素,并且不关心它们的实际存储
**下列场景考虑 `VecDeque`**
- 你想要一个可以在头尾两端插入元素的 `Vec`
- 你想要一个队列,或双端队列
**下列场景考虑`LinkedList`**
- 你非常确定你真的需要一个双向链表
**下列场景考虑 `Hashmap`**
- 你需要一个 KV 集合
- 你想要一个缓存
**下列场景考虑 `BTreeMap`**
- 你需要一个可以排序的 `HashMap`
- 你希望可以按需获取一系列元素
- 你对最小或最大的 KV 感兴趣
- 你想要寻找比某个值更大或更小的键
**下列场景考虑使用 `Set` 系列**
- 你只是需要一个 Set 集合,而不需要键值对。
**下列场景考虑使用 `BinaryHeap`**
- 你想存储一堆元素,但只想在任何给定时间内处理 最大 或 最重要的元素
- 你想要一个优先队列
## 错误处理
### P.ERR.02 当函数的返回值或者结构体字段的值可能为空时,请使用`Option<T>`
**淘汰原因**
教程向,不该放到规范中。
**【描述】**
在某些其他语言中,如果函数的返回值 或 结构体字段的值 可能为空时,通常会设置一个 “哨兵值(Sentinel Value)” 来应对这种问题,比如使用一个 `nil` 或 `-1` 等特殊值来判断这类情况。
但是,在 Rust 中不需要这样,Rust 提供了 `Option<T>` 类型就是专门用于应对这类情况。
**【正例】**
```rust
struct Config {
must: String,
opt: Option<String>,
}
// OR
fn main() {
let sentence = "The fox jumps over the dog";
let index = sentence.find("fox");
if let Some(fox) = index {
let words_after_fox = &sentence[fox..];
println!("{}", words_after_fox);
}
}
```
### P.ERR.04 当程序中需要处理错误时,应该使用 `Result<T, E>` 和 `?` 操作符
**淘汰原因**
教程向,不该放到规范中。
**【描述】**
当需要处理错误时,为了保证 程序的健壮性,应该尽可能处理错误。
**【反例】**
在实现原型类项目的时候,可以“快、糙、猛”地使用 `expect` 。但是要进生产环境,需要合理地处理错误。
```rust
let res: Result<usize, ()> = Ok(1);
res.expect("one"); // 如果有 Err, expect会 Panic !
```
**【正例】**
```rust
let res: Result<usize, ()> = Ok(1);
res?; // Ok::<(), ()>(())
```
### P.ERR.06 根据应用还是库来选择不同的错误处理方式
**淘汰原因**
最佳实践向,不该放到规范中。
**【描述】**
如果编写应用,建议使用` Error` trait对象;如果编写库,则建议返回自定义错误类型,方便下游处理
**【正例】**
```rust
// 对于应用使用 Error trait 对象更加方便
pub fn print(&self, languages: &Languages) -> Result<String, Box<dyn Error>> {
// do something
}
// 对于库,暴露自定义错误类型更加方便下游处理错误
#[derive(Debug)]
pub struct SendError<T>(pub T);
impl<T> fmt::Display for SendError<T> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(fmt, "channel closed")
}
}
```
### P.ERR.03 当程序中有不可恢复的错误时,应该让其 Panic
**淘汰原因**
教程向,不该放到规范中。
**【描述】**
如果遇到无法恢复的错误,则需要让程序 Panic。
相关 Clippy Lint: [if_then_panic](https://rust-lang.github.io/rust-clippy/master/#if_then_panic)
**【正例】**
```rust
fn boot(ptr: *const usize) {
if ptr.is_null() {
panic!("ptr is null! boot failed!")
}
// or
assert!(ptr.is_null(), "ptr is null! boot failed!");
}
```
## 多线程
### P.MTH.LOK.01 根据场景选择使用互斥锁还是 Channel
**淘汰原因**
教程向,不该放到规范中。
**【描述】**
不要从哪种方式更快的角度来考虑,而应该从使用场景。性能取决于开发者如何使用它们。
一个简单的指南:
| **Channel** 适用于 | **Mutex** 适用于 |
| ------------------------------------------------------ | ------------------------------- |
| 传递数据所有权 <br /> 分发工作单元 <br /> 传递异步结果 | 修改共享缓存<br /> 修改共享状态 |
### G.MTH.LOK.02 多线程环境下宜使用 `Arc` 代替 `Rc`
**淘汰原因**
教程向,不该放到规范中。
**【级别】** 建议
**【描述】**
`Rc` 是专门用于单线程的,多线程下应该用 `Arc` 。
**【反例】**
```rust
use std::rc::Rc;
use std::sync::Mutex;
fn foo(interned: Rc<Mutex<i32>>) { ... }
```
**【正例】**
```rust
use std::rc::Rc;
use std::sync::Arc;
use std::cell::RefCell
fn foo(interned: Rc<RefCell<i32>>) { ... }
// or
fn foo(interned: Arc<Mutex<i32>>) { ... }
```
**【Lint 检测】**
| lint name | Clippy 可检测 | Rustc 可检测 | Lint Group | level |
| ------------------------------------------------------------ | ------------- | ------------ | ----------- | ----- |
| [rc_mutex](https://rust-lang.github.io/rust-clippy/master/#rc_mutex) | yes | no | restriction | allow |
## no-std
### P.EMB.03 将一些公用的类型、函数、宏等集中到一个自定义的 `baremetal-std`
**淘汰原因**
最佳实践向,不该放到规范中。
**【描述】**
虽然 `no-std` 下不能用Rust 的标准库,但是可以自定义 `no-std` 下的标准库 `baremetal-std`,用于积累 `no-std` 下常用的公共库。
================================================
FILE: src/safe-guides/Appendix/optimizing/intro.md
================================================
# G.优化指南
---
## 内容介绍
- Rust 性能优化总则
- Rust 性能优化准备工作
- Rust 性能剖析工具介绍
- 日常 Rust 开发性能优化的技巧总结
- Rust 编译大小和编译时间优化技巧
本次分享将围绕 Rust 性能评估和调优主题,比较系统地介绍 Rust 代码的性能优化经验。先从大的总原则出发,介绍在编写 Rust 过程中应该遵循哪些原则对后续优化有帮助。接下来会分享一些代码优化的方法和技巧,然后介绍可以用于 Rust 代码性能评估的工具,也会包括 Rust专用的一些异步并发测试工具介绍。
## 引子
Rust 语言天生为并发和安全而设计,并且借鉴了面向过程/面向对象/函数式等语言的特点。Rust 的目标在性能方面对标 C 语言,但在安全和生产力方面则比 C 更胜一筹。
虽说 Rust 语言性能对标 C 语言,但开发者写出的Rust 代码如果不经任何优化,也有可能比 Python 更慢。导致 Rust 代码性能慢的因素有很多种,本文就是尝试来梳理这些情况,并且给出一套方法论和一些工具集,来帮助开发者编写高性能的 Rust 代码。
## Rust 性能优化总则
### 原则一: 不要过早优化性能
> 过早优化(Premature Optimization)
>
> Premature optimization is the root of all evil. -- DonaldKnuth
>
> 在 DonaldKnuth 的论文 《 Structured Programming With GoTo Statements 》中,他写道:"程序员浪费了大量的时间去考虑或担心程序中非关键部分的速度,而当考虑到调试和维护时,这些对效率的尝试实际上会产生强烈的负面影响。我们应该忘记这种微小的效率,比如说因为过早优化而浪费的大约97%的时间。然而,我们不应该放弃那关键的 3% 的机会"。
想把代码优化到最佳,需要花很多精力。不应该在开发的时候去想着优化的事情,不需要一步到位。先完成再完美。
但是并非所有优化过早。在编写代码的过程中,优化代码的可读性是你持续要做的。Rust 是一门讲究显式语义的语言,在命名上体现出类型的语义,对于提升可读性非常重要。
### 原则二: 不要过度优化性能
RustConf 2021 一个演讲就举了一个过度优化例子:
某个用户只是想写一些比 Python 程序性能更好的代码。第一版 Rust 实现的代码已经达到了这个要求,比 Python 代码快 20倍。但是他们花了九牛二虎之力写的第二个 Rust 版本,和第一个版本差距并不大。

性能够用就好,否则就容易浪费不必要的时间。
### 原则三: Rust 代码的性能、安全、编译速度和编译大小之间需要权衡
Rust 是同时注重安全和性能的语言。但是在优化性能的同时,是有可能损失安全性的。比如使用 Unsafe Rust 来提升性能,而忽略安全检查在某些调用环境比较安全的地方是允许的,但是并非通用的做法。所以在优化性能之前,要考虑是否要牺牲安全性。
另外 Rust 优化性能的同时,可能会导致编译速度变慢 和 编译文件大小膨胀。这也是需要权衡的地方。
## Rust 优化准备工作
在性能优化之前,你还需要做一些准备工作,用于测量你的优化是否有效。
#### 基准测试
第一步是建立一套一致的基准,可以用来确定性能的基线水平,并衡量任何渐进的改进。
> 参考:
>
> `mongodb` 的案例中,标准化的[`MongoDB` 驱动微基准集](https://github.com/mongodb/specifications/blob/master/source/benchmarking/benchmarking.rst)在这方面发挥了很好的作用,特别是因为它允许在用其他编程语言编写的`MongoDB`驱动之间进行比较。由于这些是 "微 "基准,它们还可以很容易地测量单个组件的变化(例如,读与写),这在专注于在特定领域进行改进时是非常有用的。
一旦选择了基准,就应该建立一个稳定的环境,可以用来进行所有的定时测量。确保环境不发生变化,并且在分析时不做其他 "工作"(如浏览猫的图片),这对减少基准测量中的噪音很重要。
推荐工具:
使用 cargo bench 和 [`criterion`](https://crates.io/crates/criterion) 来进行基准测试
```toml
[dev-dependencies]
criterion = { version = "0.3.5", features = ["async_tokio", "html_reports"] }
[[bench]]
name = "find"
harness = false
```
因为 Rust 自带的基准测试只能用于Nightly Rust ,所以需要使用这个第三方库 criterion 在 Stable Rust 下进行基准测试。
Criterion 会将每次运行的时间记录、分析到一个 HTML 报告中。

在报告的底部,有两个最近的运行之间的比较,较早的运行(基线)为红色,最近的运行(优化的)为蓝色。这些报告是非常有用的工具,用于可视化由于性能调整而发生的变化,并且它们对于向其他人展示结果特别有用。
它们还可以作为过去性能数据的记录,无需手动记录结果。如果有性能回归的情况,也会得到及时的反映。
#### 压力/负载测试
基准测试是开发过程中对程序性能的一种预判。而项目最终发布之后,还需要在实际环境对其进行真正的负载测试,来判断系统的延时和吞吐量。
常用的负载测试工具基本都可以使用,比如 locust,wrk之类。这里介绍一个 Rust 基金会成员公司的一个用 Rust 实现的开源分布式负载测试工具 :[goose](https://github.com/tag1consulting/goose)。
Goose 每 CPU 核产生的流量至少是 Locust 的 11 倍,对于更复杂的负载测试(例如使用第三方库抓取表单内容的负载测试),收益甚至更大。虽然 Locust 要求您管理分布式负载测试,只是为了在单个服务器上使用多个 CPU 内核,但 Goose 使用单个进程利用所有可用的 CPU 内核,从而大大简化了运行更大负载测试的过程。对代码库的持续改进继续带来新功能和更快的性能。Goose 的扩展性远远优于 Locust,可以有效地利用可用资源来实现其目标。它还支持异步流程,使更多的同步流程能够轻松且一致地从单个服务器上增加数千名用户。
Goose 拥有许多其他负载测试工具所没有的独特[调试和日志记录机制](https://book.goose.rs/logging/overview.html),简化了负载测试的编写和结果的分析。Goose 还通过对数据的多个简单视图提供了更[全面的指标](https://book.goose.rs/getting-started/metrics.html),并且可以轻松地确认负载测试在您按比例放大或缩小时按照您的预期执行。它公开了用于分配任务和任务集的算法,对操作的顺序和一致性进行[更精细的控制](https://book.goose.rs/config/scheduler.html),这对于易于重复的测试很重要。

#### 明白高性能系统的标准
在进行性能剖析之前,还应该明白高性能系统的一个标准。
性能 = 产出 / 资源消耗
产出 = 事务次数(比如,qps)和 吞吐的数据量
消耗资源 = cpu时间片,磁盘/网络 I/O 次数、流量 等
而高性能的系统是要求在固定资源消耗之下来提高产出。
对于高性能系统的设计一般遵循两个标准:
1. 最大化地利用资源。
2. 使用流水线技术减少程序中任务总耗时。比如 Rust 编译器优化编译时间,也使用了流水线技术来对crate进行并行编译。
常见瓶颈类型:
1. CPU :
1. CPU 占用过高,那么就需要减少计算的开销。
2. CPU 负载过高,那么就需要查看是否线程过多,以及多个线程的切换太过频繁,多线程交互是否有必要。
2. I/O:
1. 磁盘 IOPS(Input/Output Operations Per Second) 达到了上限。那么需要减少读写次数,提高 cache命中率。
2. IO 带宽(bandwidth) 上限。那么就需要减少磁盘的读写流量,比如使用更紧凑的数据存储格式,更小的读写放大(本来只需要读取100字节,结果触发了好多个page的读写,产生了放大的效果)。
3. I/O 并发达到上限。那么就需要考虑使用 异步I/O。
4. 锁、计时器、分页/交换等被阻塞。
## Rust 性能剖析工具介绍
在做好准备工作之后,就可以开启我们的性能剖析工作了。
性能剖析,就是要发现程序中真正存在的性能瓶颈。而不是你自以为的想象中的性能瓶颈。如果不遵守这点,就会导致过早优化或过度优化。
因为常见的性能瓶颈一般都是两类,CPU 和 I/O 。所以工具也基本面向这两类。
### On-CPU 性能剖析
#### 使用 Perf 寻找“热点”
做cpu性能剖析有很多常用的 Linux 命令行工具,比如 linux 命令行工具 perf。它功能强大:它可以检测 CPU 性能计数器、跟踪点、kprobes 和 uprobes(动态跟踪)。
你可以使用 perf 工具对 CPU 进行采样分析。以一个指定的频率对CPU进行采样,进而拿到正在CPU上运行的指令乃至整个函数调用栈的快照,最后对采样的数据分析。比如说在100次采样中有20次在运行A指令或者A函数,那么`perf`就会认为A函数的CPU使用率为20%。
可以在 Cargo.toml 中加入:
```toml
[profile.release]
debug = true
```
然后执行:
```rust
$ cargo build --release
$ perf record -g target/release/perf-test
$ perf report
```
就可以看到报告了。
#### 火焰图工具
但我们 Rust 程序中要通过[`flamegraph` ](https://crates.io/crates/flamegraph)crate,来生成 火焰图(flamegraph),它可以与`cargo`一起工作,非常方便。
因为火焰图有助于阅读源码,它以可视化的图案非常明确地展示调用栈之间的关系。火焰图可以让开发者从整体上看出各个线程的开销比例和子函数占有的比例,指引我们从整体上找到优化的优先级。
火焰图中,在被测量的执行过程中调用的每个函数会被表示为一个矩形,每个调用栈被表示为一个矩形栈。一个给定的矩形的宽度与在该函数中花费的时间成正比,更宽的矩形意味着更多的时间。火焰图对于识别程序中的慢速部分非常有用,因为它们可以让你快速识别代码库中哪些部分花费的时间不成比例。
用 Mongodb 调优的示例来说:


火焰图中的栈从底部开始,随着调用栈的加深而向上移动(左右无所谓),通常这是开始阅读它们的最佳方式。看一下上面火焰图的底部,最宽的矩形是`Future::poll`,但这并不是因为Rust 的 `Future` 超级慢,而是因为每个`.await`都涉及轮询(poll)`Future`。考虑到这一点,我们可以跳过任何轮询矩形,直到我们在`mongodb`中看到我们关心的信息的函数。
蓝色方块包含了调用`CommandResponse::body`所花费的时间,它显示几乎所有的时间都花在了`clone()`上。各个紫色矩形对应的是将`BSON`(MongoDB使用的二进制格式)解析到`Document`中所花费的时间,绿色矩形对应的是`Document`的`serde::Deserialize`实现中所花费的时间。最后,黑色虚线矩形对应的是释放内存的时间,黑色实线对应的是将命令序列化为`BSON`的时间。
所以从火焰图中反映出性能瓶颈在于:
1. Clone 过多。
2. 序列化 bson 耗费更多时间
修复完这些性能瓶颈之后,再使用基准测试测试一次。
如果可能的话,再使用 goose 这样的压测工具进行一次负载测试更好。
#### perf 适合测试 Rust 异步代码
对于异步 Rust 程序而言,火焰图的效果可能并不是很好,因为异步调度器和执行器几乎会出现在火焰图中每一块地方,看不出瓶颈所在。这个时候使用 perf 工具会更加清晰。
#### 检查内存泄露和不必要的内存分配
可以使用 **[Valgrind](https://www.valgrind.org/)** 工具来检查程序是否存在内存泄露,或者在关键的调用路径上存在不必要的内存分配。
不仅仅要考察堆分配,也需要考虑栈上的分配,特别是异步操作时。
有一个非常有用的 Rust 编译标志(仅在 Rust nightly 中可用)来验证数据结构有多大及其缓存对齐。
```rust
$ RUSTFLAGS=-Zprint-type-sizes cargo build --release
```
除了通常的 Cargo 输出之外,包括异步 Future 在内的每个数据结构都以相应的大小和缓存对齐方式打印出来。比如:
```rust
print-type-size type: `net::protocol::proto::msg::Data`: 304 bytes, alignment: 8 bytes
print-type-size field `.key`: 40 bytes
print-type-size field `.data_info`: 168 bytes
print-type-size field `.payload`: 96 bytes
```
Rust 异步编程非常依赖栈空间,异步运行时和库需要把所有东西放到栈上来保证执行的正确性。如果你的异步程序占用了过多的栈空间,可以考虑将其进行优化为 平衡的同步和异步代码组合,把特定的异步代码隔离出来也是一种优化手段。
#### 其他性能剖析/监控工具
如果允许,可以使用 英特尔出品的 [VTune ](https://www.intel.com/content/www/us/en/develop/documentation/vtune-help/top.html) 工具进行 CPU 性能剖析。
或者使用在线的性能监控平台,比如 [Logrocket](https://logrocket.com/),支持 Rust 程序,可以监控应用程序的性能,报告客户端 CPU 负载、客户端内存使用等指标。
也可以使用开源的链路追踪工具来监控你自己的 Rust 项目:使用 OpenTelemetry 标准。OpenTelemetry 也支持 Rust 。
opentelemetry是一款数据收集中间件。我们可以使用它来生成,收集和导出监测数据(Metrics,Logs and traces),这些数据可供支持OpenTelemetry的中间件存储,查询和显示,用以实现数据观测,性能分析,系统监控,服务告警等能力。
PingCAP 也开源了一款高性能的 tracing 库 : [minitrace-rust](https://github.com/tikv/minitrace-rust)
### Off-CPU 性能剖析
Off-CPU 是指在 I/O、锁、计时器、分页/交换等被阻塞的同时等待的时间。
Off-CPU 的性能剖析通常可以在程序运行过程中进行采用链路跟踪来进行分析。
还有就是使用 offcpu 火焰图进行可视化观察。

这里推荐的工具是 `eBPF`的前端工具包[bcc](https://github.com/iovisor/bcc)中的`offcputime-bpfcc`工具。
这个工具的原理是在每一次内核调用`finish_task_switch()`函数完成任务切换的时候记录上一个进程被调度离开`CPU`的时间戳和当前进程被调度到`CPU`的时间戳,那么一个进程离开`CPU`到下一次进入`CPU`的时间差即为`Off-CPU`的时间。
比如这里一段代码:
```rust
use std::io::Read;
fn test1() {
std::thread::sleep(std::time::Duration::from_nanos(200));
}
fn test2() {
let mut f = std::fs::File::open("./1.txt").unwrap();
let mut buffer = Vec::new();
f.read_to_end(&mut buffer).unwrap();
}
fn main() {
loop {
test1();
test2();
}
}
```
程序中一共有两种会导致进程被调度出`CPU`的任务,一个是`test1()`函数中的`sleep()`,一个是在`test2()`函数中的读文件操作。
这里需要使用debug编译,因为`offcputime-bpfcc`依赖于`frame pointer`来进行栈展开,所以我们需要开启`RUSTFLAGS="-C force-frame-pointers=yes"`的编译选项以便打印出用户态的函数栈。我们使用如下的命令获取`Off-CPU`的分析数据。
```rust
$ ./target/debug/mytest & sudo offcputime-bpfcc -p `pgrep -nx mytest` 5
```
然后使用 火焰图工具将其生成 off-cpu 火焰图:
```rust
$ git clone https://github.com/brendangregg/FlameGraph
$ cd FlameGraph
$ sudo offcputime-bpfcc -df -p `pgrep -nx mytest` 3 > out.stacks
$ ./flamegraph.pl --color=io --title="Off-CPU Time Flame Graph" --countname=us < out.stacks > out.svg
```
得到下面火焰图:

与`On-CPU`的火焰图相同,纵轴代表了函数调用栈,横轴代表了`Off-CPU`时间的比例,跨度越大代表`Off-CPU`的时间越长。
### 其他适合 Rust 性能剖析的工具介绍
除了 perf 和 火焰图 工具,下面还有一些 Rust 程序适用的工具。
- [Hotspot](https://github.com/KDAB/hotspot)和[Firefox Profiler](https://profiler.firefox.com/)是查看perf记录的数据的好工具。
- [Cachegrind](https://www.valgrind.org/docs/manual/cg-manual.html)和[Callgrind](https://www.valgrind.org/docs/manual/cl-manual.html)给出了全局的、每个函数的、每个源线的指令数以及模拟的缓存和分支预测数据。
- [DHAT](https://www.valgrind.org/docs/manual/dh-manual.html)可以很好的找到代码中哪些部分会造成大量的分配,并对峰值内存使用情况进行深入了解。
- [heaptrack](https://github.com/KDE/heaptrack)是另一个堆分析工具。
- [`counts`](https://github.com/nnethercote/counts/)支持即席(*Ad Hoc*)剖析,它将`eprintln!`语句的使用与基于频率的后处理结合起来,这对于了解代码中特定领域的部分内容很有帮助。
- [Coz](https://github.com/plasma-umass/coz)执行*因果分析*以衡量优化潜力。它通过[coz-rs](https://github.com/plasma-umass/coz/tree/master/rust)支持Rust。因果分析技术可以找到程序的瓶颈并显示对其进行优化的效果。
## 日常 Rust 开发性能优化技巧总结
虽然我们需要通过完善的性能测试方法来剖析系统中存在的瓶颈,保证不会过早优化和过度优化。但是在日常编码过程中,Rust 社区内也总结出来一些优化技巧来供参考:
### 1. 对于只被调用一次的函数可能并不需要进行优化。
比如读取配置文件,这种多慢都没有关系。
不要只优化程序中最慢的函数,要优化占用大部分运行时间的函数。
在一个被调用 1000 次的函数上得到 2 毫秒的改进,那比在一个被调用一次的函数上获得 1 秒的改进要好。
### 2. 优先改进你的算法
很多时候性能不佳,很可能是由于算法不佳而不是实现不佳。请检查你的代码中循环的使用,只需尝试尽可能少的循环。
1. 记住每次使用`collect`必须至少会迭代整个集合一次,所以最好只 collect 一次。
2. 警惕你使用的标准库方法和第三方库方法内部实现中隐藏的循环。
### 3. 要充分理解 Rust 中数据结构的内存布局
要学会区分 Rust 中数据类型的内存布局,它们在栈上和堆上如何分配的。
比如 `String`,`Vec`,`HashMap`和`Box<Trait>`/`Box<[T]>`所有分配都在堆上。
在栈上分配的数据,移动的时候只能是 按位复制的方式。所以即便内存是在栈上分配,也要考虑这个 Copy 的成本。
堆上的数据,要尽可能地避免深拷贝(显式 Clone) 。
并且要尽可能地缓存数据,而避免频繁的内存分配发生。比如可以使用诸如 slab 之类的第三方库,可以合理复用内存。
### 4. 避免 `Box<Trait>` 动态分发
创建 trait 对象的规范方法是`Box<Trait>`,但大多数代码都可以使用`&mut Trait`,它也具有动态分派但节省了分配。如果您绝对需要所有权,请使用`Box`,但大多数用例都可以使用`&Trait`或`&mut Trait`。
有些场景也可以使用 Enum 来代替 trait 对象。参见 [`enum_dispatch`](https://docs.rs/enum_dispatch/latest/enum_dispatch/)。
### 5. 使用基于栈的可变长度数据类型
定长度的数据类型可以简单地存储在堆栈上,但对于动态大小的数据,它并不是那么简单。但是,[`smallvec`](https://github.com/servo/rust-smallvec), [`smallstring`](https://github.com/jFransham/smallstring)和[`tendril`](https://github.com/servo/tendril)都是可变长度数据类型,允许在栈上存储少量元素。像`smallvec`这样的库非常适合缓存局部性,可以减少分配。
```rust
// This is a gross oversimplification of how this type is implemented in the// crate, but it's enough to explain how it works.enum SmallVec<T> { Small([T; 4]), Big(Vec<T>),}type Matrix<T> = SmallVec<SmallVec<T>>;
```
### 6. 合理使用断言避免数组越界检查
Safe Rust 会被编译器自动塞入数组越界检查,比如下面代码:
```rust
fn do_something_with_array(array: &[u8]) -> u8 { array[0] + array[1] + array[2] + array[3] + array[4] + array[5]}
```
可以通过编译输出 MIR 看到,编译器会给数组索引访问插入断言检查:
```rust
assert(move _9, "index out of bounds: the length is {} but the index is {}", move _8, _7)
```
有几个数组索引访问就会被插入几次,上面的代码会被插入 6 次,这极大影响性能。

所以我们可以手工插入一次断言检查,就可以消除编译器的自动插入。
```rust
fn do_something_with_array(array: &[u8]) -> u8 { assert!(array.len >= 5); array[0] + array[1] + array[2] + array[3] + array[4] + array[5]}
```
这一条也是可以举一反三的,比如 Rust 也会为普通的加法操作添加防止计算溢出的断言,但是你如何手工使用了 wrapped_add 之类的方法,那就可以避免编译器自动插入这类断言。
### 7. 使用链接时优化(LTO)
链接时优化允许编译器跨 crate 进行内联,但是这样做的代价是减慢编译时间。但我认为,编译时间如何能换取性能提升,那么这个时间值得牺牲。
### 8. 不要使用 `#[inline(always)]`
Rust 编译器自身的优化可以计算出何时需要内联一些函数,不需要你手工明确指定。除非这个函数调用十分频繁。
因为这种显式的指定会导致编译大小的膨胀,如果你的硬件资源不受限可能不太重要。但是对于资源受限的环境,比如嵌入式,则需要进行权衡。
对于一些小的函数,如果没有使用 LTO,但是需要跨 crate 内联的话,也可以显式指定 `#[inline]`。
### 9. 避免显式 Clone
尽可能地使用引用,避免过多的 Clone 。因为Clone 可能伴随内存分配。
### 10. 使用 Unsafe 方法消除一些不必要的安全检查
在 Rust 标准库中,你可以看到很多 `_unchecked`后缀的方法。
比如 `String::from_utf8` 和 `String::from_utf8_unchecked`,是一对 Safe 和 Unsafe 的方法。
一般情况下,应该使用 `String::from_utf8` 将 `u8`序列转换为合法的字符串,这个方法对 `u8`序列进行了合法 utf8编码的检查。但是这个检查也会有一定开销。
如果开发者能确保调用环境的 `u8`序列来源是完全合法的 utf8 编码,那么这个安全检查就完全可以忽略。此时就可以使用 `String::from_utf8_unchecked` 来替换 `String::from_utf8` 用来提升性能。
```rust
pub fn from_utf8(vec: Vec<u8>) -> Result<String, FromUtf8Error> { match str::from_utf8(&vec) { Ok(..) => Ok(String { vec }), Err(e) => Err(FromUtf8Error { bytes: vec, error: e }), }}pub unsafe fn from_utf8_unchecked(bytes: Vec<u8>) -> String { String { vec: bytes }}
```
### 11. 并发/并行化你的程序
用 Rust 写多线程和异步并发程序是非常便利的。
推荐的库有很多:
- [rayon](https://github.com/rayon-rs/rayon),并行迭代器
- [crossbeam](https://docs.rs/crossbeam/latest/crossbeam/) / [flume](https://github.com/zesterer/flume),多线程channel/ 无锁并发结构
- [Tokio](https://github.com/tokio-rs/tokio) ,高性能异步运行时
- [loom](https://github.com/tokio-rs/loom), Tokio 提供的并发代码测试工具,支持 C11 内存模型。
- [console](https://github.com/tokio-rs/console),Tokio 提供的 Rust 异步诊断和调试工具,可以将其视为异步代码的 Clippy。通过监控应用程序中任务的运行时操作,可以检测*可能*暗示错误或性能问题的行为模式,并突出显示它们以供用户分析。
- 跨平台 SIMD,并行化你的计算。
### 12. 并发程序中,合理使用锁,或替换无锁数据结构
在某些场景中,可能读并发访问要比写并发更频繁,那么可以用 读写锁来替换互斥锁。另外,使用第三方库 parking_lot 中定义的并发锁来代替标准库中的锁。
或者合理选择无锁数据结构来替换用锁来同步的数据结构,并不是说无锁一定比锁同步性能更好,也是需要看场景和选择高质量的第三方实现。
### 13. 使用 Clippy
使用 Clippy 工具对代码进行静态分析,它可以针对性能改进提供一些建议。
关于 Clippy 性能改进 lint 可以在这里找到:[https://rust-lang.github.io/rust-clippy/master/index.html](https://rust-lang.github.io/rust-clippy/master/index.html)
同样可以遵循 [ Rust 编码规范 ](https://rust-coding-guidelines.github.io/rust-coding-guidelines-zh/) 中的一些规范,也会包括 Clippy 的一些建议。如果你有什么性能优化的小技巧,欢迎提交贡献。
## 编译大小和编译时间的优化总结
### 1. 优化编译大小
- 设置 codegen-units=1 ,codegen-units 叫做代码生成单元,Rust 编译器会把crate 生成的 LLVMIR进行分割,默认分割为16个单元,每个单元就叫 codegen-units,如果分割的太多,就不利于 Rust编译器使用内联优化一些函数调用,分割单元越大,才越容易判断需要内联的地方。但是这也有可能增大编译文件大小,需要大小和性能间寻找平衡。
- 设置panic=abort。可以缩减编译文件的大小。
- 设置编译优化等级为 `z`,意为最小二进制体积。编译器的优化级别对应的是`LLVM`函数内联的阈值,`z` 对应的是 25,而 级别 `3`则对应 275 。
- 评估代码中泛型和宏的使用,是否可以精简
- 其他参考:https://github.com/johnthagen/min-sized-rust
### 2. 优化编译大小的一些技巧
- 使用 cargo check 代替 cargo build
- 使用最新 Rust 工具链
- 使用 Rust Analyzer 而不是 Rust Language Server (RLS)
- 删除未使用的依赖项
- 替换依赖过多的第三方库
- 使用 workspace,将项目拆分为多个crate,方便并行编译
- 将针对模块的测试单独拆分为一个测试文件
- 将所有集成测试组合在一个文件中
- 禁止 crate 依赖未使用功能
- 使用 ssd或Ramdisk(虚拟内存盘) 进行编译
- 使用 [sccache](https://github.com/mozilla/sccache) 缓存依赖项
- 切换到更快的链接器:[mold](https://github.com/rui314/mold) (Linux)/ [zld](https://github.com/michaeleisel/zld) (MacOS) / 🤷 (Windows),可以使用以下命令检查链接所花时间:
```rust
cargo cleancargo +nightly rustc --bin <your_binary_name> -- -Z time-passes
```
- Rust 针对 MacOS 用户也提升了增量编译性能,在 Cargo.toml 中进行以下配置:
- ```rust
[profile.dev]split-debuginfo = "unpacked"
```
- 调整更多 Codegen 选项/编译器标志。这是[完整的 codegen 选项列表](https://doc.rust-lang.org/rustc/codegen-options) 。为了获得灵感,这里是[bevy 的用于更快编译的配置](https://github.com/bevyengine/bevy/blob/3a2a68852c0a1298c0678a47adc59adebe259a6f/.cargo/config_fast_builds)。
- 剖析文件编译时间。使用 [`cargo rustc -- -Zself-profile`](https://blog.rust-lang.org/inside-rust/2020/02/25/intro-rustc-self-profile.html#profiling-the-compiler)生成的跟踪文件可以使用火焰图或 Chromium 分析器进行可视化。还有一个[`cargo -Z timings`](https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#timings)功能可以提供有关每个编译步骤需要多长时间的一些信息,并随着时间的推移跟踪并发信息。
- 避免过程宏 Crates,主要是因为使用了 syn 。过程宏是 Rust 开发的热点:它们会消耗 CPU 周期,因此请谨慎使用。serde 库中包含了过程宏,它在很多地方都用到,所以需要注意是否一定需要serde 进行序列化和反序列化。
- 避免过多的泛型。过多的泛型单态化也会导致编译时间增加。
- 提升你的硬件,或者在云端(比如[Gitpod.io](https://gitpod.io/),可免费使用 16 核 Intel Xeon 2.80GHz,60GB RAM的主机)使用更好的硬件环境进行编译。
- 下载所有的依赖 crate。编译过程中有很大一部分时间用于下载,提前下载好crate是有帮助的。参考 https://github.com/the-lean-crate/criner
- 使用 docker 进行编译。[`cargo-chef`](https://www.lpalmieri.com/posts/fast-rust-docker-builds/)可用于充分利用 Docker 层缓存,从而大大加快 Rust 项目的 Docker 构建。
- 超频 cpu ?谨慎。
- 优化 CI 构建速度。参考 https://matklad.github.io/2021/09/04/fast-rust-builds.html。
- 你自己开发 crate 的时候尽量保持精简,利人利己。
## 参考
1. [https://zenoh.io/blog/2021-07-13-zenoh-performance-async/](https://zenoh.io/blog/2021-07-13-zenoh-performance-async/)
2. [https://bheisler.github.io/criterion.rs/book/getting_started.html](https://bheisler.github.io/criterion.rs/book/getting_started.html)
3. [https://rust-coding-guidelines.github.io/rust-coding-guidelines-zh/safe-guides/Appendix/test/benchmark.html#%E7%94%A8-cargo-bench-%E5%92%8C-criterionrs-%E6%9D%A5%E6%89%A7%E8%A1%8C%E5%9F%BA%E5%87%86%E6%B5%8B%E8%AF%95](https://rust-coding-guidelines.github.io/rust-coding-guidelines-zh/safe-guides/Appendix/test/benchmark.html#%E7%94%A8-cargo-bench-%E5%92%8C-criterionrs-%E6%9D%A5%E6%89%A7%E8%A1%8C%E5%9F%BA%E5%87%86%E6%B5%8B%E8%AF%95)
4. [https://gist.github.com/jFransham/369a86eff00e5f280ed25121454acec1](https://gist.github.com/jFransham/369a86eff00e5f280ed25121454acec1)
5. [https://github.com/tag1consulting/goose](https://github.com/tag1consulting/goose)
6. [https://rustmagazine.github.io/rust_magazine_2021/chapter_11/rust-profiling.html?search=](https://rustmagazine.github.io/rust_magazine_2021/chapter_11/rust-profiling.html?search=)
7. [https://rustmagazine.github.io/rust_magazine_2021/chapter_7/paper-rust-vs-c.html](https://rustmagazine.github.io/rust_magazine_2021/chapter_7/paper-rust-vs-c.html)
8. [https://blues-star.github.io/perf-book-zh/benchmarking_zh.html](https://blues-star.github.io/perf-book-zh/benchmarking_zh.html)
9. [https://en.pingcap.com/blog/how-we-trace-a-kv-database-with-less-than-5-percent-performance-impact/](https://en.pingcap.com/blog/how-we-trace-a-kv-database-with-less-than-5-percent-performance-impact/)
10. [https://rust-coding-guidelines.github.io/rust-coding-guidelines-zh/](https://rust-coding-guidelines.github.io/rust-coding-guidelines-zh/)
11. [https://endler.dev/2020/rust-compile-times/](https://endler.dev/2020/rust-compile-times/)
12. [https://github.com/johnthagen/min-sized-rust](https://github.com/johnthagen/min-sized-rust)
13. [https://docs.rust-embedded.org/book/unsorted/speed-vs-size.html](https://docs.rust-embedded.org/book/unsorted/speed-vs-size.html)
14. [https://fasterthanli.me/articles/why-is-my-rust-build-so-slow](https://fasterthanli.me/articles/why-is-my-rust-build-so-slow)
================================================
FILE: src/safe-guides/Appendix/rustc-flag.md
================================================
# H.Rust编译器编译参数说明
## Rustc 说明
通过以下命令可以打印编译器相关参数选项:
```rust
$ rustc -h
// 或
$ rustc --help
// 在 Nightly Rust 下,比 Stable Rust 多一个 `-Z` 参数,用于传递 unstable compiler options
```
## 参数功能
### `-h` / `--help`
用于打印 `rustc` 的帮助信息
### `--cfg`
用于打开或关闭 `#[cfg]` 变量中的各种条件编译设置。
```rust
--cfg 'verbose' // 对应 #[cfg(verbose)]
// or
--cfg 'feature="serde"' // 对应 #[cfg(feature = "serde")]
```
### `-L`
`-L` 用于将一个目录添加到外部搜索路径。命令格式如下:
```rust
-L [KIND=]PATH
```
当 `KIND` 是以下情况之一时,可以使用 `-L KIND=PATH `这种形式指定搜索路径的类型:
- `dependency` — 仅在该目录中搜索传递依赖项。
- `crate` — 仅在该目录中搜索此 crate 的直接依赖项。
- `native` — 仅在该目录中搜索本地库。
- `framework` — 仅用于在该目录中搜索 macOS 框架。
- `all` — 搜索此目录中的所有库类型。如果 KIND 没有指定,这将是默认值.
### `-l`
用于将生成的 crate 链接到本地库。
```rust
-l [KIND[:MODIFIERS]=]NAME[:RENAME]
```
当 `KIND` 是以下情况之一时,库的种类可以使用 `-l KIND=lib` 这种形式指定:
- `dylib` — 本地动态库
- `static` — 本地静态库 (例如 .a archive 文件)
- `framework` — macOS 框架
可以用`#[link]`属性指定库的种类。 如果未在 `link `属性或命令行中指定 `KIND` ,它将链接到可用动态库,否则将使用静态库。 如果在命令行中指定了库类型,其将会覆盖 `link` 属性指定的库类型。
`link` 属性中使用的名称可以使用形如` -l ATTR_NAME:LINK_NAME` 形式覆盖,其中 `ATTR_NAME` 是 `link` 属性中的名称,`LINK_NAME `是将要链接到的实际库的名称。
#### `--crate-type`
这将指示 rustc 以何种 crate type 去构建。该 Flag 接收逗号分隔的值列表,也可以多次指定。有效的 crate type 如下:
- `lib` — 编译器生成的首选库类型, 目前默认为 rlib。
- `rlib` — Rust 静态库。
- `staticlib` — 本地静态库。
- `dylib` — Rust 动态库。
- `cdylib` — 本地动态库。
- `bin` — 可执行程序。
- `proc-macro` — 生成格式化且编译器可加载的过程宏库。
可以使用 `crate_type`属性来指定 crate 类型,注意 `--crate-type` 命令行的值会覆盖 crate_type 属性的值。更多细节可以参阅 reference 中的[ 链接章节](https://doc.rust-lang.org/stable/reference/linkage.html)。
#### `--crate-name`
用于指定正在构建的 crate 名称
#### `--edition`
用于指定编译器使用哪个 版次(edition),可选项 `2015|2018|2021`。
#### `--emit`
```rust
--emit [asm|llvm-bc|llvm-ir|obj|metadata|link|dep-info|mir]
```
该 Flag 控制编译器生成的输出文件的类型。其接收以逗号分隔的值列表,也可以多次指定。有效的生成类型有:
- `asm` — 生成在 crate 中的一个汇编代码文件。 默认的输出文件是 CRATE_NAME.s。
- `dep-info` — 生成一个包含 Makefile 语法的文件,指示加载以生成 crate 的所有源文件。 默认输出文件是 CRATE_NAME.d。
- `link` — 生成由 --crate-type 指定的 crates 。 默认输出文件取决于平台和 crate 类型。 如果未指定 --emit 这将是默认值。
- `llvm-bc` — 生成一个包含 LLVM bitcode 的二进制文件。默认输出文件是 CRATE_NAME.bc。
- `llvm-ir` — 生成一个包含 LLVM IR( LLVM 中间语言)的文件。默认的输出文件是 CRATE_NAME.ll。
- `metadata` — 生成一个关于该 crate 的元数据的文件。 默认输出文件是 CRATE_NAME.rmeta。
- `mir` — 生成一个包含 Rust 中级中间表示(即中级中间语言)的文件。默认输出文件名是 `CRATE_NAME.mir`。
- `obj` — 生成一个本地对象文件,默认输出文件是 `CRATE_NAME.o`。
输出文件名可以用 `-o flag` 进行设置。使用` -C extra-filename`。Flag 可以添加文件名后缀。文件将被写入当前目录除非使用` --out-dir flag ` 标签。 每一个生成类型也可以使用 `KIND=PATH `的形式指定输出文件名,它优先于 `-o `标签。
### `--print`
用于打印有关编译器的各种信息。指定` --print `标签通常会禁用 `--emit `步骤且只打印请求的信息。打印的有效值类型为:
- `crate-name` — crate 的名称。
- `file-names` — 文件名由 link 命令执行的种类所决定。
- `sysroot` — 系统根目录路径,即`.rustup` 下的 `toolchains` 文件夹。
- `target-libdir` - 目标` lib `文件夹路径(同上)。
- `cfg` — 条件编译值列表。 了解更多条件编译值信息,请参阅 条件编译。
- `target-list` — 已知目标列表。 可以使用`--target `标签选择目标。
- `target-cpus `— 当前目标的可用 CPU 值列表。可以使用 `-C target-cpu=val` Flag 选择目标。
- `target-features`— 当前目标的可用 目标 `features` 列表。目标 `features` 可以使用` -C target-feature=val `flag 启用。该Flag是不安全( unsafe )的。
- `relocation-models` — 重定位模型列表。重定位模型可以用 `-C relocation-model=val ` Flag 选择。
- `code-models` — 代码模型列表。代码模型可以用` -C code-model=val` flag 进行设置。
- `tls-models` — 支持的线程本地存储模型列表。 模型可以用` -Z tls-model=val ` Flag 来选择(仅限 Nightly Rust)。
- `native-static-libs` — 当创建一个` staticlib crate` 类型时可以使用此选项。 如果这是唯一的标志,它将执行一个完整的编译,并包含一个指出链接生成静态库时要使用的链接器 Flag 的诊断说明。该说明以文本 `native-static-libs:` 开始,以便更容易获取输出信息。
### `-g`
等价于 `-C debuginfo=2`,用于输出调试信息。
**调试信息说明:**
- `0`:根本没有调试信息(默认)。
- `1`: 仅行表。
- `2`:完整的调试信息。
### `-O` (大写的字母o,优化)
等价于 `-C opt-level=2`,用于编译优化,level为2。
**优化级别说明:**
- `0`:没有优化,也打开 [`cfg(debug_assertions)`](https://doc.rust-lang.org/rustc/codegen-options/index.html#debug-assertions)(默认)。
- `1`: 基本优化。
- `2`: 一些优化。
- `3`: 所有优化。
- `s`: 优化二进制大小。
- `z`:优化二进制大小,但也关闭循环向量化。
默认值为`0`.
### `-o`
用于控制输出的文件名。
#### `--out-dir DIR`
用于指定输出目录位置。
#### `--explain OPT`
用于提供错误消息的详细说明。`rustc` 对于每一个(检测到的)错误都会返回一个错误码,这将打印给定错误的更详细说明。
#### `--test`
构建测试工具。
#### `--target TARGET`
指定编译的` target triple` 。
### 设置 lint
#### `-W`
等价于 `--warn LINT` ,设置给定 lint 为 warning 级别。
#### `-A`
等价于 `--allow LINT` ,设置给定 lint 为 allow 级别。
#### `-D`
等价于 `--deny LINT` ,设置给定 lint 为 deny 级别。
#### `-F`
等价于 `--forbid LINT` ,设置给定 lint 为 forbid 级别。
### `-C`
用于设置 [代码生成](https://doc.rust-lang.org/rustc/codegen-options/index.html) 选项。可以通过 `rustc -C help` 进一步查看其选项。
#### 已弃用参数
- `ar=val`,废弃。
- `no-stack-check=val`,废弃。
#### `code-model` (优化相关)
`code-model=val`,支持的值为:
- `tiny` - 微小的代码模型。
- `small`- 小代码模型。这是大多数受支持目标的默认模型。
- `kernel` - 内核代码模型。
- `medium` - 中等代码模型。
- `large` - 大型代码模型。比如 x86 平台上,告诉编译器不要进行任何假设,使用64位绝对取址模型访问代码及数据。
可以通过命令 `rustc --print code-models` 查看。
代码模型对程序及其符号可能使用的地址范围施加了限制。对于较小的地址范围,机器指令可能能够使用更紧凑的寻址模式。具体范围取决于目标架构和可用的寻址模式。
代码模型是程序员与编译器间的一个正式的协议,其中程序员表达其对最终程序将进入的当前正在编译的目标文件大小的意愿。比如,“不要担心,这个对象只会进入不那么大的程序,因此你可以使用快速的RIP相对取址模式”,相反,“这个对象期望链接进巨大的程序,因此请使用慢但安全的,带有完整64位偏移的绝对取址模式”。
#### `codegen-units` (优化: 性能 vs 编译速度)
`codegen-units=val`,这个值表示控制 crate 分成多少个代码生成单元(codegen units),它需要一个大于 0 的整数。
当一个 crate 被拆分为多个代码生成单元时,LLVM 能够并行处理它们。增加并行度可能会**加快编译时间**,但也可能会**产生更慢的代码**,因为可能会影响内联(inline)优化。
将此设置为 `1` 可能会提高生成代码的性能,但编译速度可能会变慢。
如果未指定,则默认值为 16,用于非增量构建。对于增量构建,默认值为 256,这允许缓存更细粒度。
#### `control-flow-guard` (安全)
此Flag 控制 LLVM 是否启用 Windows [控制流保护(Control Flow Guard)](https://docs.microsoft.com/en-us/windows/win32/secbp/control-flow-guard) 平台安全功能。对于非 Windows 目标,此标志当前被忽略。它采用以下值之一:
- `y`, `yes`, `on`, `checks`, 或无值:表示启用控制流防护。
- `nochecks`:在没有运行时强制检查的情况下发出控制流保护元数据(这应该只用于测试目的,因为它不提供安全强制)。
- `n`, `no`, `off`: 不启用控制流保护(默认)。
关于 Rust 和 CFG 的更详细说明参考:
- [Control Flow Guard for Clang/LLVM and Rust](https://msrc-blog.microsoft.com/2020/08/17/control-flow-guard-for-clang-llvm-and-rust/)
- [UnStable book: control-flow-guard](https://doc.rust-lang.org/beta/unstable-book/compiler-flags/control-flow-guard.html)
#### `debug-assertions`
`debug-assertions=val`,用于打开或关闭 `cfg(debug_assertions)` 条件编译。采用以下的值:
- `y`, `yes`, `on`, 或无值:启用调试断言。
- `n`, `no`, or `off`: 禁用调试断言。
如果无指定,调试断言会在 [opt-level](https://doc.rust-lang.org/rustc/codegen-options/index.html#opt-level) 为 0 的优化级别下默认开启。所以这里需要注意优化级别,debug_assertions 在 release 编译时最好不要开启。这个和 `debug_assert!` 宏相关。
#### `default-linker-libraries`
`default-linker-libraries=val`,用于设置链接器是否可以链接它的默认库,可以设置的值为:
- `y`, `yes`, `on`, 或无值:包括默认库(默认)。
- `n`, `no`, or `off`: 排除默认库。
#### `embed-bitcode` (优化:编译大小 && 编译时间)
`embed-bitcode=val`,控制编译器是否将 LLVM 位码嵌入到目标文件中。它采用以下值之一:
- `y`, `yes`, `on`, 或无值:将位码放入 rlibs(默认)。
- `n`, `no`, or `off`: 从 rlibs 中省略位码。
`rustc` 执行链接时优化 (LTO) 时需要 LLVM 位码(bitcode)。嵌入的位码将出现在 rustc 生成的目标文件中,该文件的名称由目标平台定义。大多数时候是这样 `.llvmbc`。
如果你的编译实际不需要位码,使用`-C embed-bitcode=no` 可以显著提高编译时间并减少生成的文件大小(例如,如果你不为iOS编译或者你不执行LTO)。由于这些原因,Cargo尽可能地使用`-C embed-bitcode=no`。同样地,如果你直接用 rustc 构建,我们建议在不使用 LTO 时使用 `-C embed-bitcode=no`。
如果与`-C lto`结合,`-C embed-bitcode=no`将导致`rustc`在启动时中止,因为这种结合是无效的。
> bitcode是由LLVM引入的一种中间代码(Intermediate Representation,简称IR),它是源代码被编译为二进制机器码过程中的中间表示形态,它既不是源代码,也不是机器码。从代码组织结构上看它比较接近机器码,但是在函数和指令层面使用了很多高级语言的特性。LLVM 优化器负责对bitcode进行各种类型的优化,将bitcode代码进行一些逻辑等价的转换,使得代码的执行效率更高,体积更小,比如DeadStrip/SimplifyCFG,LLVM 后端负责把优化后的bitcode编译为指定目标架构的机器码。
>
> 来源:[https://xelz.info/blog/2018/11/24/all-you-need-to-know-about-bitcode/](https://xelz.info/blog/2018/11/24/all-you-need-to-know-about-bitcode/)
#### `force-frame-pointers` (优化:性能)
`force-frame-pointers=val`,用于设置是否强制启用 帧指针(frame-pointers)。相当于 Clang 的`-fno-omit-frame-pointer`,
它采用以下值之一:
- `y`, `yes`, `on`, 或无值:强制启用帧指针。
- `n`, `no`, or `off`: 不要强制启用帧指针。这并不一定意味着将删除帧指针。
如果未强制启用帧指针,则默认行为取决于 target。
一般情况下,如果设置 `force-frame-pointers=no` 是一种帧指针省略优化。它造成的弊大于利,默认情况下不应启用。
#### `force-unwind-tables` (优化:编译大小)
`force-unwind-tables=val`,强制生成 unwind 表。它采用以下值之一:
- `y`, `yes`, `on`, 或无值:强制生成 unwind 表。
- `n`, `no`, or `off`: 不强制生成 unwind 表。如果目标需要 unwind 表,则会发出错误。
如果未指定,则默认值取决于 target 。
打开 `force-unwind-tables=on` 可能会导致二进制编译大小膨胀,对于移动和嵌入式这种二进制大小很重要的场景下,建议启用该选项。
#### `incremental`(优化:编译时间)
`incremental=val` ,用于启用增量编译。
#### `inline-threshold` (优化:性能)
`inline-threshold=val`, 允许您设置内联函数的默认阈值。它接受一个无符号整数作为值。内联基于成本模型(cost model),其中较高的阈值将允许更多内联。
默认值取决于[opt-level](https://doc.rust-lang.org/rustc/codegen-options/index.html#opt-level):
| 选择级别 | 临界点 |
| -------- | -------------------------------------------------- |
| 0 | 不适用,仅内联始终内联函数 |
| 1 | 不适用,仅内联始终内联函数和 LLVM 生命周期内在函数 |
| 2 | 225 |
| 3 | 275 |
| s | 75 |
| z | 25 |
#### 链接相关参数
- `link-arg=val`,将单个额外参数附加到链接器调用。
- `link-args=val`,将多个额外参数附加到链接器调用。选项应该用空格分隔。
- `link-dead-code=val`,控制链接器是否保留死代码,尝试构建代码覆盖率指标时,此标志可能有用。它采用以下值之一:
- `y`, `yes`, `on`, 或无值:保留死代码。
- `n`, `no`, or `off`: 删除死代码(默认)。
- ` link-self-contained=val`,控制链接器是使用 Rust 附带的库和对象还是系统中的库和对象。
- `linker=val `,控制链接器`rustc`调用哪个链接器来链接您的代码。它采用链接器可执行文件的路径。如果未指定此标志,则将根据目标推断链接器。选择特定的链接器,有助于**优化编译时间**。
- `linker-flavor=val`,链接器带有[`-C linker`标志](https://doc.rust-lang.org/rustc/codegen-options/index.html#linker),则链接器风格是从提供的值中推断出来的。如果没有给出链接器,则使用链接器风格来确定要使用的链接器。每个`rustc`目标都默认为某种链接器风格。选项参考:[linker-flavor](https://doc.rust-lang.org/rustc/codegen-options/index.html#linker-flavor) 。
- `linker-plugin-lto=val`,允许将 LTO 优化推迟到实际的链接步骤,如果所有被链接的目标文件都是由基于 LLVM 的工具链创建的,那么这反过来又允许跨编程语言边界执行过程间**优化**。详细介绍参见[Linker-plugin-LTO](https://doc.rust-lang.org/rustc/linker-plugin-lto.html)。它采用以下值之一:
- `y`, `yes`, `on`, 或无值:启用链接器插件 LTO。
- `n`, `no`, or `off`: 禁用链接器插件 LTO(默认)。
- 链接器插件的路径。
#### `lto` (优化:性能)
`lto=val`,这个标志控制LLVM是否使用链接时优化(link-time optimizations),以产生更好的优化代码,使用整个程序分析,代价是延长链接时间,所以,会减慢编译时间。它取以下值之一:
- `y`, `yes`,` on`,` fat`, or `no`值:执行 "fat "LTO,试图在依赖图中的所有crate中进行优化。
- `n`,` no`,` of`f: 禁用LTO。
- `thin`:执行 "Thin "LTO。这与 "fat "类似,但运行时间大大减少,同时仍能实现与 "fat "类似的性能提升。
如果没有指定`-C lto`,那么编译器将尝试执行 "thin local LTO",它只在本地的crate上对其代码单元执行 "thin "LTO。当没有指定`-C lto`时,如果代码单元是`1`或者优化被禁用(`-C opt-level=0`),LTO将被禁用。
即:
- 当没有指定`-C lto`时:
- `codegen-units=1`: 禁用LTO
- `opt-level=0`: 禁用LTO。
- 当`-C lto`被指定时:
- `lto`:16个代码生成单元,在整个crate 中执行 fat LTO。
- `codegen-units=1` +` lto`: `1`个编码生成单元,跨 crate 进行fat LTO。
跨语言的LTO 参见[Linker-plugin-LTO](https://doc.rust-lang.org/rustc/linker-plugin-lto.html) 。
#### `passes` (优化:性能)
`passes=val`,设置额外的 LLVM passes 列表,使用空格分隔。
> LLVM Pass 是LLVM代码优化(optimization)中的一个重要组成部分。为便于理解,可以将Pass看作一个又一个的模块,各个Pass可以通过IR获取信息为下一个Pass做好准备,又或者直接对中间代码进行优化。
>
> 总的来说,所有的pass大致可以分为两类:
>
> - 分析(`analysis`)和转换分析类的pass以提供信息为主
> - 转换类(`transform`)的pass优化中间代码
>
> 详细参考:[代码优化与LLVM IR pass](https://kiprey.github.io/2020/06/LLVM-IR-pass/)
相关选项:`no-prepopulate-passes=val`,使用空的 Pass 列表。
#### `no-redzone`
`no-redzone=val`,允许禁用[红区](https://en.wikipedia.org/wiki/Red_zone_(computing))。它采用以下值之一:
- `y`, `yes`, `on`, 或无值:禁用红色区域。
- `n`, `no`, 或`off`: 启用红色区域。
> **红区**(redzone)是System V ABI提供的一种优化的产物,它允许函数无需调整**栈指针**(stack pointer),便能临时使用其**栈帧**(stack frame)下方的128个字节。红区被定义为调整过的栈指针下方128个字节的区域——函数将会使用这个区域,来存放一些无需跨越函数调用的临时数据。因此,在一些情况下,比如在小的**叶函数**(leaf function)[1]中,我们可以优化掉调整栈指针所需的两条指令。(
>
> **叶函数**(leaf function)指的是不调用其它函数的函数;可以理解为,是函数调用图的叶子节点。特别地,**尾递归函数**(tail recursive function)的尾部可以看作是叶函数。
>
> 然而,当**异常**(exception)或**硬件中断**(hardware interrupt)发生时,这种优化却可能产生严重的问题。
>
> 参考:[使用Rust编写操作系统(附录二):禁用红区](https://github.com/rustcc/writing-an-os-in-rust/blob/master/appendix-b-red-zone.md)
#### `no-vectorize-loops` (优化:性能)
`no-vectorize-loops=val`,禁用循环矢量化(loop vectorization optimization passes)。等价于 Clang 的 `-fno-vectorize`。
LLVM Loop Vectorizer 具有许多功能,可以对复杂的循环进行矢量化(向量化即“批量操作”,数据并行)。循环矢量化器使用成本模型来决定最佳矢量化因子和展开因子。但是,矢量化器的用户可以强制矢量化器使用特定值。许多循环无法向量化,包括具有复杂控制流、不可向量化类型和不可向量化调用的循环。对于更复杂的情况,我们则需要手动进行SIMD编程。
是否禁用,取决于你的使用场景。
相关:`no-vectorize-slp=val`,禁用 SLP 向量化。
#### ` overflow-checks` (安全)
` overflow-checks=val `,控制[运行时整数溢出检查](https://doc.rust-lang.org/reference/expressions/operator-expr.html#overflow),启用溢出检查时,溢出时会发生恐慌。取值为:
采用以下值之一:
- `y`, `yes`, `on`, 或无值:启用溢出检查。
- `n`, `no`, or `off`: 禁用溢出检查。
如果未指定,则在启用 [调试断言](https://doc.rust-lang.org/rustc/codegen-options/index.html#debug-assertions)时启用溢出检查,否则禁用。
#### `panic`
控制代码恐慌时发生的情况:
- `abort`: 恐慌时终止进程,不能执行析构函数。
- `unwind`: 恐慌时展开堆栈,可以执行析构函数,以及 catch_unwind 。
如果未指定,则默认值取决于目标。
#### ` prefer-dynamic`
` prefer-dynamic=val`,默认情况下,`rustc`更喜欢静态链接依赖项。如果库的静态和动态版本都可用,此选项将指示应尽可能使用动态链接。有一个内部算法用于确定是否可以静态或动态地与依赖项链接。例如,`cdylib` crate类型可能只使用静态链接。此标志采用以下值之一:
- `y`, `yes`, `on`, 或无值:使用动态链接。
- `n`, `no`, or `off`: 使用静态链接(默认)。
#### `profile-generate` (优化)
`profile-generate=val`,允许创建检测二进制文件,这些二进制文件将收集分析数据以用于 Profile Guided Optimization (PGO)。该Flag 采用一个可选参数,该参数是已检测二进制文件将向其中发出收集的数据的目录的路径。有关详细信息,请参阅 [profile-guided optimization](https://doc.rust-lang.org/rustc/profile-guided-optimization.html) 。
相关:`profile-use=val`。
#### `split-debuginfo` (调试)
`split-debuginfo=val `,控制`rustc`生成的调试信息的“split debuginfo”的发射。此选项的默认行为是特定于平台的,并非此选项的所有可能值都适用于所有平台。可能的值为:
- `off`- 这是具有 ELF 二进制文件和 windows-gnu(不是 Windows MSVC 和 macOS)的平台的默认设置。这通常意味着可以在可执行文件部分的最终工件中找到 DWARF 调试信息。Windows MSVC 不支持此选项。在 macOS 上,此选项可防止最终执行`dsymutil`生成调试信息。
- `packed`- 这是 Windows MSVC 和 macOS 的默认设置。这里的“packed”一词意味着所有调试信息都打包到与主可执行文件不同的文件中。在 Windows MSVC 上这是一个`*.pdb`文件,在 macOS 上这是一个`*.dSYM`文件夹,而在其他平台上这是一个`*.dwp` 文件。
- `unpacked`- 这意味着调试信息将在每个编译单元(目标文件)的单独文件中找到。Windows MSVC 不支持此功能。在 macOS 上,这意味着原始目标文件将包含调试信息。在其他 Unix 平台上,这意味着`*.dwo`文件将包含调试信息。
请注意,此时`packed`和`unpacked`在`-Z unstable-options`非 macOS 平台上被关闭。
#### `strip` (优化:编译大小)
`strip=val`,控制在链接期间从二进制文件中剥离调试信息和类似的辅助数据。可用于减少编译文件大小。
此选项支持的值为:
- `none`- 根据目标将调试信息和符号(如果存在)复制到生成的二进制文件或单独的文件中(例如`.pdb`,MSVC 中的文件)。
- `debuginfo` - debuginfo 部分和符号表部分中的 debuginfo 符号在链接时被剥离,并且不会复制到生成的二进制文件或单独的文件中。
- `symbols`- 与 相同`debuginfo`,但如果链接器支持,符号表部分的其余部分也会被剥离。
### `-Z`
`-Z` Flag 只允许在 Nightly Rust 下使用,因为它包含了 未稳定的编译器选项。
在该 Flag 下面有许多未稳定的子参数,这里就不一一列举。只挑选几个和安全相关的展示一下:
- `stack-protector=val` ,用于控制栈粉碎保护策略,用于缓冲溢出保护。通过命令 `rustc --print stack-protector-strategies`可以看到详细设置值。
- `sanitizer=val`,sanitizers(CFI 除外)的实现几乎完全依赖于 LLVM,将来增加 GCC 后端应该可以多一些支持。用于支持 内存错误检测器([AddressSanitizer](https://clang.llvm.org/docs/AddressSanitizer.html))、LLVM 控制流完整性 (CFI) 提供前沿控制流保护([ControlFlowIntegrity](https://clang.llvm.org/docs/ControlFlowIntegrity.html) )、运行时内存泄漏检测器([LeakSanitizer](https://clang.llvm.org/docs/LeakSanitizer.html))、未初始化读取的检测器([MemorySanitizer](https://clang.llvm.org/docs/MemorySanitizer.html))、数据竞争检测器([ThreadSanitizer](https://clang.llvm.org/docs/ThreadSanitizer.html))等。
## 参考
https://doc.rust-lang.org/rustc/command-line-arguments.html
https://doc.rust-lang.org/rustc/codegen-options/index.html
================================================
FILE: src/safe-guides/Appendix/templates/clippy.toml.md
================================================
# Clippy 模板
有些 Clippy 的 Lint,依赖于一些配置项,如果不想要默认值,可以在 `clippy.toml` 中进行设置。
```toml
# for `disallowed_method`:
# https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_method
disallowed-methods = []
# 函数参数最长不要超过5个
too-many-arguments-threshold=5
```
## Clippy lint 配置模板
```rust
// 参考: https://github.com/serde-rs/serde/blob/master/serde/src/lib.rs
#![allow(unknown_lints, bare_trait_objects, deprecated)]
#![cfg_attr(feature = "cargo-clippy", allow(renamed_and_removed_lints))]
#![cfg_attr(feature = "cargo-clippy", deny(clippy, clippy_pedantic))]
// Ignored clippy and clippy_pedantic lints
#![cfg_attr(
feature = "cargo-clippy",
allow(
// clippy bug: https://github.com/rust-lang/rust-clippy/issues/5704
unnested_or_patterns,
// clippy bug: https://github.com/rust-lang/rust-clippy/issues/7768
semicolon_if_nothing_returned,
// not available in our oldest supported compiler
checked_conversions,
empty_enum,
redundant_field_names,
redundant_static_lifetimes,
// integer and float ser/de requires these sorts of casts
cast_possible_truncation,
cast_possible_wrap,
cast_sign_loss,
// things are often more readable this way
cast_lossless,
module_name_repetitions,
option_if_let_else,
single_match_else,
type_complexity,
use_self,
zero_prefixed_literal,
// correctly used
enum_glob_use,
let_underscore_drop,
map_err_ignore,
result_unit_err,
wildcard_imports,
// not practical
needless_pass_by_value,
similar_names,
too_many_lines,
// preference
doc_markdown,
unseparated_literal_suffix,
// false positive
needless_doctest_main,
// noisy
missing_errors_doc,
must_use_candidate,
)
)]
// Rustc lints.
#![deny(missing_docs, unused_imports)]
```
## Embark Studios 的标准 Lint 配置
```rust
// BEGIN - Embark standard lints v5 for Rust 1.55+
// do not change or add/remove here, but one can add exceptions after this section
// for more info see: <https://github.com/EmbarkStudios/rust-ecosystem/issues/59>
#![deny(unsafe_code)]
#![warn(
clippy::all,
clippy::await_holding_lock,
clippy::char_lit_as_u8,
clippy::checked_conversions,
clippy::dbg_macro,
clippy::debug_assert_with_mut_call,
clippy::disallowed_method,
clippy::disallowed_type,
clippy::doc_markdown,
clippy::empty_enum,
clippy::enum_glob_use,
clippy::exit,
clippy::expl_impl_clone_on_copy,
clippy::explicit_deref_methods,
clippy::explicit_into_iter_loop,
clippy::fallible_impl_from,
clippy::filter_map_next,
clippy::flat_map_option,
clippy::float_cmp_const,
clippy::fn_params_excessive_bools,
clippy::from_iter_instead_of_collect,
clippy::if_let_mutex,
clippy::implicit_clone,
clippy::imprecise_flops,
clippy::inefficient_to_string,
clippy::invalid_upcast_comparisons,
clippy::large_digit_groups,
clippy::large_stack_arrays,
clippy::large_types_passed_by_value,
clippy::let_unit_value,
clippy::linkedlist,
clippy::lossy_float_literal,
clippy::macro_use_imports,
clippy::manual_ok_or,
clippy::map_err_ignore,
clippy::map_flatten,
clippy::map_unwrap_or,
clippy::match_on_vec_items,
clippy::match_same_arms,
clippy::match_wild_err_arm,
clippy::match_wildcard_for_single_variants,
clippy::mem_forget,
clippy::mismatched_target_os,
clippy::missing_enforced_import_renames,
clippy::mut_mut,
clippy::mutex_integer,
clippy::needless_borrow,
clippy::needless_continue,
clippy::needless_for_each,
clippy::option_option,
clippy::path_buf_push_overwrite,
clippy::ptr_as_ptr,
clippy::rc_mutex,
clippy::ref_option_ref,
clippy::rest_pat_in_fully_bound_structs,
clippy::same_functions_in_if_condition,
clippy::semicolon_if_nothing_returned,
clippy::single_match_else,
clippy::string_add_assign,
clippy::string_add,
clippy::string_lit_as_bytes,
clippy::string_to_string,
clippy::todo,
clippy::trait_duplication_in_bounds,
clippy::unimplemented,
clippy::unnested_or_patterns,
clippy::unused_self,
clippy::useless_transmute,
clippy::verbose_file_reads,
clippy::zero_sized_map_values,
future_incompatible,
nonstandard_style,
rust_2018_idioms
)]
// END - Embark standard lints v0.5 for Rust 1.55+
// crate-specific exceptions:
#![allow()]
```
## Clippy 配置的相关问题
目前 Clippy 不支持配置文件来配置Lint ,目前 像 Embark 公司有两种解决方法:
1. 将 lint 放到一个[统一文件](https://github.com/EmbarkStudios/rust-ecosystem/blob/main/lints.rs)中,然后复制粘贴到使用的地方。
2. 通过 `.cargo/config.toml` 来配置 `rustflags` ,参考: [lints.toml](https://github.com/EmbarkStudios/rust-ecosystem/blob/main/lints.toml)
Embark 也在跟踪和推动在 Cargo 中支持 Lint 配置的功能,相关 issues:
- [Be able to disable/enable Clippy lints globally](https://github.com/EmbarkStudios/rust-ecosystem/issues/22)
- [Support defining enabled and disabled lints in a configuration file](https://github.com/rust-lang/cargo/issues/5034)
- [[Roadmap] Configuration file for lints](https://github.com/rust-lang/rust-clippy/issues/6625)
## 代码生成相关 clippy 配置
和 C 语言绑定代码生成,避免clippy 警告,相关配置可参考:
```rust
// Generated by gir (https://github.com/gtk-rs/gir @ 5bbf6cb)
// from ../gir-files (@ 8e47c67)
// DO NOT EDIT
#![allow(non_camel_case_types, non_upper_case_globals, non_snake_case)]
#![allow(clippy::approx_constant, clippy::type_complexity, clippy::unreadable_literal, clippy::upper_case_acronyms)]
```
================================================
FILE: src/safe-guides/Appendix/templates/deny.toml.md
================================================
# Cargo Deny 配置模板
[cargo-deny](https://github.com/EmbarkStudios/cargo-deny) 是检查 Cargo 依赖的一个 Lint 工具。它检查的范围包括:
- Licenses,检查依赖crate许可证是否合规。
- Bans, 检查被禁止使用的依赖 crate。
- Advisories ,检查有安全缺陷漏洞或停止维护的 依赖 crate。
- Source,检查依赖crate 的来源,确保只来自于可信任的来源。
以下是模板(参考 [vectordotdev/vector 的 deny.toml](https://github.com/vectordotdev/vector/blob/master/deny.toml)):
```toml
[licenses]
allow = [
"MIT",
"CC0-1.0",
"ISC",
"OpenSSL",
"Unlicense",
"BSD-2-Clause",
"BSD-3-Clause",
"Apache-2.0",
"Apache-2.0 WITH LLVM-exception",
"Zlib",
]
unlicensed = "warn"
default = "warn"
private = { ignore = true }
[[licenses.clarify]]
name = "ring"
version = "*"
expression = "MIT AND ISC AND OpenSSL"
license-files = [
{ path = "LICENSE", hash = 0xbd0eed23 }
]
[advisories]
ignore = [
# term is looking for a new maintainer
# https://github.com/timberio/vector/issues/6225
"RUSTSEC-2018-0015",
# `net2` crate has been deprecated; use `socket2` instead
# https://github.com/timberio/vector/issues/5582
"RUSTSEC-2020-0016",
# Type confusion if __private_get_type_id__ is overriden
# https://github.com/timberio/vector/issues/5583
"RUSTSEC-2020-0036",
# stdweb is unmaintained
# https://github.com/timberio/vector/issues/5585
"RUSTSEC-2020-0056",
]
```
================================================
FILE: src/safe-guides/Appendix/templates/intro.md
================================================
# D.模版
这里记录一些 rustfmt 和 clippy 等相关工具等配置文件模版。
- [rustfmt](./rustfmt.toml.md)
- [clippy](./clippy.toml.md)
- [deny](./deny.toml.md)
================================================
FILE: src/safe-guides/Appendix/templates/rustfmt.toml.md
================================================
# Rustfmt 模板
为了方便 Rust 开发者,这里提供一个 Rustfmt 的模板,以供参考。
以下内容可以放到 `rustfmt.toml` 或 `.rustfmt.toml` 文件中。因为部分选项还未稳定,所以要使用 `cargo +nightly fmt` 执行。
很多选项都是默认的,无需配置。以下配置的都不是默认值。
**【只包含 Stable 的选项】**
```toml
# 万一你要使用 rustfmt 2.0 就需要指定这个·
version = "Two"
# 统一管理宽度设置,但不包含 comment_width
use_small_heuristics="MAX"
# 在match分支中,如果包含了块,也需要加逗号以示分隔
match_block_trailing_comma=true
# 当使用 extern 指定外部函数时,不需要显式指定 C-ABI ,默认就是 C-ABI
force_explicit_abi=false
# 如果项目只在 Unix 平台下跑,可以设置该项为 Unix,表示换行符只依赖 Unix
newline_style="Unix"
# 不要将多个 Derive 宏合并为同一行
merge_derives = false
# 指定 fmt 忽略的目录
ignore = [
"src/test",
"test",
"docs",
]
```
**【也包含还未 Stable 的选项】**
未稳定,代表该选项还有一些 issue 没有解决,待解决以后就会稳定。
```toml
# 万一你要使用 rustfmt 2.0 就需要指定这个·
version = "Two"
# 统一管理宽度设置,但不包含 comment_width
use_small_heuristics="MAX"
# 使多个标识符定义保持对齐风格,代码看上去可以非常工整
indent_style="Visual" # 未稳定
# 设置让自定义具有判别式的枚举体按等号对齐的宽度
enum_discrim_align_threshold = 10 # 未稳定
# 在match分支中,如果包含了块,也需要加逗号以示分隔
match_block_trailing_comma=true
# 自动将同一个 crate 的模块导入合并到一起
imports_granularity="Crate" # 未稳定
# StdExternalCrate 导入模块分组规则
# 1. 导入来自 std、core 和 alloc 的模块需要置于前面一组。
# 2. 导入来自 第三方库的模块 应该置于中间一组。
# 3. 导入来自本地 self、super和crate前缀的模块,置于后面一组。
group_imports="StdExternalCrate" # 未稳定
# format_macro_matchers 规则说明:
# 声明宏 模式匹配分支(=> 左侧)中要使用紧凑格式
# 默认声明宏分支代码体(=> 右侧) 使用宽松格式
format_macro_matchers=true # 未稳定
# 当使用 extern 指定外部函数时,不需要显式指定 C-ABI ,默认就是 C-ABI
force_explicit_abi=false
# 指定一行注释允许的最大宽度
comment_width=100 # 未稳定
# wrap_comments 配合 comment_width 使用,自动将一行超过宽带限制的注释切分为多行注释
wrap_comments=true # 未稳定
# 将 /**/ 注释转为 //
normalize_comments=true # 未稳定
# 元组模式匹配的时候允许使用 `..` 来匹配剩余元素
condense_wildcard_suffixes=true # 未稳定
# 如果项目只在 Unix 平台下跑,可以设置该项为 Unix,表示换行符只依赖 Unix
newline_style="Unix"
# 不要将多个 Derive 宏合并为同一行
merge_derives = false
# 指定 fmt 忽略的目录
ignore = [
"src/test",
"test",
"docs",
]
```
================================================
FILE: src/safe-guides/Appendix/terms.md
================================================
# C.术语解释
## 语言元素术语表
术语 | 中文翻译 | 备注
------------------------------- |----------------------------- |----------
**A** | |
Abstract Syntax Tree | 抽象语法树 |
ABI | 应用程序二进制接口 | Application Binary Interface 缩写
accumulator | 累加器 |
accumulator variable | 累加器变量 |
ahead-of-time compiled | 预编译 |
ahead-of-time compiled language | 预编译语言 |
alias | 别名 |
aliasing | 别名使用 | 参见 [Wikipedia](https://en.wikipedia.org/wiki/Pointer_aliasing)
angle brackets | 尖括号,“<”和“>” |
annotate | 标注,注明(动词) |
annotation | 标注,注明(名词) |
Arc | 原子引用计数器 | Atomic Referecne Counter
anonymity | 匿名 |
argument | 参数,实参,实际参数 | 不严格区分的话, argument(参数)和 <br> parameter(参量)可以互换地使用
argument type | 参数类型 |
assignment | 赋值 |
associated functions | 关联函数 |
associated items | 关联项 |
associated types | 关联类型 |
asterisk | 星号(\*) |
atomic | 原子的 |
attribute | 属性 |
automated building | 自动构建 |
automated test | 自动测试,自动化测试 |
**B** | |
benchmark | 基准 |
binary | 二进制的 |
binary executable | 二进制的可执行文件 |
bind | 绑定 |
block | 语句块,代码块 |
boolean | 布尔型,布尔值 |
borrow check | 借用检查 |
borrower | 借用者,借入者 |
borrowing | 借用 |
bound | 约束,限定,限制 | 此词和 constraint 意思相近,<br>constraint 在 C# 语言中翻译成“约束”
box | 箱子,盒子,装箱类型 | 一般不译,作动词时翻译成“装箱”,<br>具有所有权的智能指针
boxed | 装箱,装包 |
boxing | 装箱,装包 |
brace | 大括号,“{”或“}” |
buffer | 缓冲,缓冲区,缓冲器,缓存 |
build | 构建 |
builder pattern | 创建者模式 |
**C** | |
call | 调用 |
caller | 调用者 |
capacity | 容器 |
capture | 捕获 |
cargo | (Rust 包管理器,不译) | 该词作名词时意思是“货物”,<br>作动词时意思是“装载货物”
cargo-fy | Cargo 化,使用 Cargo 创建项目 |
case analysis | 事例分析 |
cast | 类型转换,转型 |
casting | 类型转换 |
chaining method call | 链式方法调用 |
channel | 信道,通道 |
closure | 闭包 |
coercion | 强制类型转换,强制转换 | coercion 原意是“强制,胁迫”
collection | 集合 | 参见 [Wikipedia](https://zh.wikipedia.org/wiki/%E9%9B%86%E5%90%88_(%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6)) |
combinator | 组合算子,组合器 |
comma | 逗号,“,” |
command | 命令 |
command line | 命令行 |
comment | 注释 |
compile | 编译(动词) |
compile time | 编译期,编译期间,编译时 |
compilation | 编译(名词) |
compilation unit | 编译单元 |
compiler | 编译器 |
compiler intrinsics | 编译器固有功能 |
compound | 复合(类型,数据) |
concurrency | 并发 |
conditional compilation | 条件编译 |
configuration | 配置 |
constructor | 构造器 |
consumer | 消费者 |
container | 容器 |
container type | 容器类型 |
convert | 转换,转化,转 |
copy | 复制,拷贝 |
crate | 包,包装箱,装包 | 一般不译,crate 是 Rust 的基本编译单元
curly braces | 大括号,包含“{”和“}” |
custom type | 自定义类型 |
**D** | |
dangling pointer | 悬垂指针 | use after free 在释放后使用
data race | 数据竞争 |
dead code | 死代码,无效代码,不可达代码 |
deallocate | 释放,重新分配 |
declare | 声明 |
deep copy | 深拷贝,深复制 |
dependency | 依赖 |
deref coercions | 强制多态 |
dereference | 解引用 | Rust 文章中有时简写为 Deref
derive | 派生 |
designator | 指示符 |
destruction | 销毁,毁灭 |
destructor | 析构器,析构函数 |
destructure | 解构 |
destructuring | 解构,解构赋值 |
desugar | 脱糖 |
diverge function | 发散函数 |
device drive | 设备驱动 |
directory | 目录 |
dispatch | 分发 |
diverging functions | 发散函数 |
documentation | 文档 |
dot operator | 点运算符 |
DST | 动态大小类型 | dynamic sized type,一般不译,<br>使用英文缩写形式
dynamic language | 动态类型语言 |
dynamic trait type | 动态特质类型 |
declarative macros | 声明宏 |
**E** | |
enumeration | 枚举 |
encapsulation | 封装 |
equality test | 相等测试 |
elision | 省略 |
exhaustiveness checking | 穷尽性检查,无遗漏检查 |
expression | 表达式 |
expression-oriented language | 面向表达式的语言 |
explicit | 显式 |
explicit discriminator | 显式的辨别值 |
explicit type conversion | 显式类型转换 |
extension | 扩展名 |
extern | 外,外部 | 作关键字时不译
**F** | |
fat pointer | 胖指针 |
feature gate | 功能开关 |
field | 字段 |
field-level mutability | 字段级别可变性 |
file | 文件 |
fmt | 格式化,是 format 的缩写 |
formatter | 格式化程序,格式化工具,格式器|
floating-point number | 浮点数 |
flow control | 流程控制 |
Foreign Function Interface(FFI)| 外部语言函数接口 |
fragment specifier | 片段分类符 |
free variables | 自由变量 |
freeze | 冻结 |
function | 函数 |
function declaration | 函数声明 |
functional | 函数式 |
**G** | |
garbage collector | 垃圾回收 |
generalize | 泛化,泛型化 |
generator | 生成器 |
generic | 泛型 |
generic type | 泛型类型 |
growable | 可增长的 |
guard | 守卫 |
**H** | |
handle error | 句柄错误 |
hash | 哈希,哈希值,散列 |
hash map | 散列映射,哈希表 |
heap | 堆 |
hierarchy | 层次,分层,层次结构 |
higher rank lifetime | 高阶生命周期 |
higher rank trait bound | 高阶特质约束 |
higher tank type | 高阶类型 |
hygiene | 卫生 |
hygienic macro system | 卫生宏系统 |
**I** | |
ICE | 编译内部错误 | internal comppiler error 的缩写
immutable | 不可变的 |
implement | 实现 |
implementor | 实现者 |
implicit | 隐式 |
implicit discriminator | 隐式的辨别值 |
implicit type conversion | 隐式类型转换 |
import | 导入 |
in assignment | 在赋值(语句) |
index | 索引 | 英语复数形式:indices
infer | 推导(动词) |
inference | 推导(名词) |
inherited mutability | 承袭可变性 |
inheritance | 继承 |
integrated development <br>environment(IDE) | 集成开发环境 | 中文著作中通常直接写成 IDE
integration-style test | 集成测试 |
interior mutability | 内部可变性 |
installer | 安装程序,安装器 |
instance | 实例 |
instance method | 实例方法 |
integer | 整型,整数 |
interact | 相互作用,相互影响 |
interior mutability | 内部可变性 |
intrinsic | 固有的 |
invoke | 调用 |
item | 项,条目,项目 |
iterate | 重复 |
iteration | 迭代 |
iterator | 迭代器 |
iterator adaptors | 迭代器适配器 |
iterator invalidation | 迭代器失效 |
**L** | |
LHS | 左操作数 | left-hand side 的非正式缩写,<br>与 RHS 相对
lender | 借出者 |
library | 库 |
lifetime | 生存期/ 寿命 / 生命周期 |
lifetime elision | 生命周期省略 |
link | 链接 |
linked-list | 链表 |
lint | (不译) | lint 英文本义是“纱布,绒毛”,此词在<br>计算机领域中表示程序代码中可疑和<br>不具结构性的片段,参见 [Wikipedia](https://en.wikipedia.org/wiki/Lint_%28software%29) |
list | 列表 |
listener | 监听器 |
literal | 数据,常量数据,字面值,字面量,<br>字面常量,字面上的 | 英文意思:字面意义的(内容)
LLVM | (不译) | Low Level Virtual Machine 的缩写,<br>是构建编译器的系统
loop | 循环 | 作关键字时不译
low-level code | 底层代码 |
low-level language | 底层语言 |
l-value | 左值 |
**M** | |
main function | main 函数,主函数 |
macro | 宏 |
map | 映射 | 一般不译
match guard | 匹配守卫 |
memory | 内存 |
memory leak | 内存泄露 |
memory safe | 内存安全 |
meta | 原则,元 |
metadata | 元数据 |
metaprogramming | 元编程 |
metavariable | 元变量 |
method call syntax | 方法调用语法 |
method chaining | 方法链 |
method definition | 方法定义 |
modifier | 修饰符 |
module | 模块 |
monomorphization | 单态 | mono: one, morph: form
move | 移动,转移 | 按照 Rust 所规定的内容,<br>英语单词 transfer 的意思<br>比 move 更贴合实际描述<br>参考:[Rust by Example](http://rustwiki.org/rust-by-example/scope/move.html)
move semantics | 移动语义 |
mutability | 可变性 |
mutable | 可变 |
mutable reference | 可变引用 |
multiple bounds | 多重约束 |
mutiple patterns | 多重模式 |
**N** | |
nest | 嵌套 |
Nightly Rust | Rust 开发版 | nightly本意是“每夜,每天晚上”,<br>指代码每天都更新
NLL | 非词法生命周期 | non lexical lifetime 的缩写,<br>一般不译
non-copy type | 非复制类型 |
non-generic | 非泛型 |
no-op | 空操作,空运算 | (此词出现在类型转换章节中)
non-commutative | 非交换的 |
non-scalar cast | 非标量转换 |
notation | 符号,记号 |
numeric | 数值,数字 |
**O** | |
optimization | 优化 |
out-of-bounds accessing | 越界访问 |
orphan rule | 孤儿规则 |
overflow | 溢出,越界 |
own | 占有,拥有 |
owner | 所有者,拥有者 |
ownership | 所有权 |
**P** | |
package manager | 包管理器,软件包管理器 |
panic | (不译) | 此单词直接翻译是“恐慌”,<br>在 Rust 中用于不可恢复的错误处理
parameter | 参量,形参,形式参量 | 不严格区分的话, argument(参数)和 <br> parameter(参量)可以互换地使用
parametric polymorphism | 参数多态 |
parent scope | 父级作用域 |
parentheses | 小括号,包括“(”和“)” |
parse | 分析,解析 |
parser | (语法)分析器,解析器 |
pattern | 模式 |
pattern match | 模式匹配 |
phantom type | 虚类型,虚位类型 | phantom 相关的专有名词:<br>phantom bug 幻影指令<br>phantom power 幻象电源<br>参见:[Haskell](https://wiki.haskell.org/Phantom_type)、[Haskell/Phantom_type](https://en.wikibooks.org/wiki/Haskell/Phantom_types)、<br>[Rust/Phantom](http://rustwiki.org/rust-by-example/generics/phantom.html)、[stdlib/PhantomData](https://doc.rust-lang.org/std/marker/struct.PhantomData.html)
platform | 平台 |
polymorphism | 多态 |
powershell |(不译) | Windows 系统的一种命令行外壳程序<br>和脚本环境
possibility of absence | 不存在的可能性 |
precede | 预先?,在...发生(或出现) |
prelude |(不译) | 预先导入模块,英文本意:序曲,前奏
primitive types | 原生类型,基本类型,简单类型 |
print | 打印 |
process | 进程 |
procedural macros | 过程宏,程序宏 |
project | 项目,工程 |
prototype | 原型 |
**R** | |
race condition | 竞态条件 |
RAII | 资源获取即初始化(一般不译) | resource acquisition is initialization 的缩写
range | 区间,范围 |
range expression | 区间表达式 |
raw identifier | 原始标识符 |
raw pointer | 原始指针,裸指针 |
RC | 引用计数 | reference counted
Reader | 读取器 |
recursive macro | 递归宏 |
reference | 引用 |
reference cycle | 引用循环 |
release | 发布 |
resource | 资源 |
resource leak | 资源泄露 |
RHS | 右操作数 | right-hand side 的非正式缩写,<br>与 LHS 相对
root directory | 根目录 |
runtime | 运行时 |
runtime behavior | 运行时行为 |
runtime overhead | 运行时开销 |
Rust | (不译) | 一种编程语言
Rustacean | (不译) | 编写 Rust 的程序员或爱好者的通称
rustc | (不译) | Rust 语言编译器
r-value | 右值 |
**S** | |
scalar | 标量,数量 |
schedule | 调度 |
scope | 作用域 |
screen | 屏幕 |
script | 脚本 |
semicolon | 分号,“;” |
self | 自身,作关键字时不译 |
shadow | 遮蔽,隐蔽,隐藏,覆盖 |
shallow copy | 浅拷贝,浅复制 |
signature | 标记 |
slice | 切片 |
snake case | 蛇形命名 | 参见:[Snake case](https://en.wikipedia.org/wiki/Snake_case)
source file | 源文件 |
source code | 源代码 |
specialization | 泛型特化 |
square | 平方,二次方,二次幂 |
square brackets | 中括号,“[”和“]” |
src | (不译) | source 的缩写,指源代码
stack | 栈 |
stack unwind | 栈解开、栈展开 |
statement | 语句 |
statically allocated | 静态分配 |
statically allocated string | 静态分配的字符串 |
statically dispatch | 静态分发 |
static method | 静态方法 |
string | 字符串 |
string literal | 字符串常量 |
string slices | 字符串片段 |
stringify | 字符串化 |
subscript notation | 下标 |
sugar | 糖 |
super | 父级,作关键字时不译 |
syntax context | 语法上下文 |
systems programming language | 系统级编程语言 |
**T** | |
tagged union | 标记联合 |
target triple | 多层次指标,三层/重 指标/目标 | triple 本义是“三”,但此处虚指“多”,<br>此词翻译需要更多讨论
terminal | 终端 |
testing | 测试 |
testsuit | 测试套件 |
the least significant bit (LSB) | 最低数字位 |
the most significant bit (MSB) | 最高数字位 |
thread | 线程 |
TOML | (不译) | Tom's Obvious, Minimal Language <br>的缩写,一种配置语言
token tree | 令牌树? | 待进一步斟酌
trait | 特质 | 其字面上有“特性,特征”之意
trait bound | 特质约束 | bound 有“约束,限制,限定”之意
trait object | 特质对象 |
transmute | (不译) | 其字面上有“变化,变形,变异”之意,<br>不作翻译
trivial | 平凡的 |
troubleshooting | 疑难解答,故障诊断,<br>故障排除,故障分析 |
tuple | 元组 |
two's complement | 补码,二补数 |
two-word object | 双字对象 |
type annotation | 类型标注 |
type erasure | 类型擦除 |
type inference | 类型推导 |
type inference engine | 类型推导引擎 |
type parameter | 类型参量 |
type placeholder | 类型占位符 |
type signature | 类型标记 |
**U** | |
undefined behavior | 未定义行为 |
uninstall | 卸载 |
unit-like struct | 类单元结构体 |
unit struct | 单元结构体 |
"unit-style" tests | 单元测试 |
unit test | 单元测试 |
unit type | 单元类型 |
universal function call syntax <br>(UFCS) | 通用函数调用语法 |
unsized types | 不定长类型 |
unwind | 展开 |
unwrap | 解包 | 暂译!
**V** | |
variable binding | 变量绑定 |
variable shadowing | 变量遮蔽,变量隐蔽,<br>变量隐藏,变量覆盖 |
variable capture | 变量捕获 |
variant | 变量 |
vector | (动态数组,一般不译) | vector 本义是“向量”
visibility | 可见性 |
vtable | 虚表 |
**W** | |
where clause | where 子句,where 从句,where 分句 | 在数据库的官方手册中多翻译成“子句”,英语语法中翻译成“从句”
wrap | 包裹 | 暂译!
wrapped | 装包 |
wrapper | 装包 |
**Y** | |
yield | 产生(收益、效益等),产出,提供|
**Z** | |
zero-cost abstractions | 零开销抽象 |
zero-width space(ZWSP) | 零宽空格 |
**参考**
[Rust 语言术语中英文对照表](https://github.com/rust-lang-cn/english-chinese-glossary-of-rust/blob/master/rust-glossary.md)
## 编译器相关术语表
术语 | 中文 | 意义
------------------------------------------------------|--------|--------
<span id="arena">arena/arena allocation</span> | <span id="arena">竞技场分配</span> | arena 是一个大内存缓冲区,从中可以进行其他内存分配,这种分配方式称为竞技场分配。
<span id="ast">AST</span> | <span id="ast">抽象语法树</span> | 由`rustc_ast` crate 产生的抽象语法树。
<span id="binder">binder</span> | <span id="binder">绑定器</span> | 绑定器是声明变量和类型的地方。例如,`<T>` 是`fn foo<T>(..)`中泛型类型参数 `T`的绑定器,以及 \|`a`\|` ...` 是 参数`a`的绑定器。
<span id="body-id">BodyId</span> | <span id="body-id"> 主体ID</span> | 一个标识符,指的是crate 中的一个特定主体(函数或常量的定义)。
<span id="bound-var">bound variable</span> | <span id="bound-var">绑定变量</span> | "绑定变量 "是在表达式/术语中声明的变量。例如,变量`a`被绑定在闭包表达式中\|`a`\|` a * 2`。
<span id="codegen">codegen</span> | <span id="codegen">代码生成</span> |由 MIR 转译为 LLVM IR。
<span id="codegen-unit">codegen unit</span> | <span id="codegen-unit">代码生成单元</span> | 当生成LLVM IR时,编译器将Rust代码分成若干个代码生成单元(有时缩写为CGU)。这些单元中的每一个都是由LLVM独立处理的,实现了并行化。它们也是增量编译的单位。
<span id="completeness">completeness</span> | <span id="completeness">完整性</span> | 类型理论中的一个技术术语,它意味着每个类型安全的程序也会进行类型检查。同时拥有健全性(soundness)和完整性(completeness)是非常困难的,而且通常健全性(soundness)更重要。
<span id="cfg">control-flow graph</span> | <span id="cfg">控制流图</span> | 程序的控制流表示。
<span id="ctfe">CTFE</span> | <span id="ctfe">编译时函数求值</span> | 编译时函数求值(Compile-Time Function Evaluation)的简称,是指编译器在编译时计算 "const fn "的能力。这是编译器常量计算系统的一部分。
<span id="cx">cx</span> | <span id="cx">上下文</span> | Rust 编译器内倾向于使用 "cx "作为上下文的缩写。另见 "tcx"、"infcx "等。
<span id="ctxt">ctxt</span> | <span id="ctxt">上下文(另一个缩写)</span> | 我们也使用 "ctxt "作为上下文的缩写,例如, [`TyCtxt`](#TyCtxt),以及 [cx](#cx) 或 [tcx](#tcx)。
<span id="dag">DAG</span> | <span id="dag">有向无环图</span> | 在编译过程中,一个有向无环图被用来跟踪查询之间的依赖关系
<span id="data-flow">data-flow analysis</span> | <span id="data-flow">数据流分析</span> | 静态分析,找出程序控制流中每一个点的属性。
<span id="debruijn">DeBruijn Index</span> | <span id="debruijn">德布鲁因索引</span> | 一种只用整数来描述一个变量被绑定的绑定器的技术。它的好处是,在变量重命名下,它是不变的。
<span id="def-id">DefId</span> | <span id="def-id">定义Id</span> | 一个识别定义的索引(见`rustc_middle/src/hir/def_id.rs`)。`DefPath`的唯一标识。
<span id="discriminant">discriminant</span> | <span id="discriminant">判别式</span> | 与枚举变体或生成器状态相关的基础值,以表明它是 "激活的(avtive)"(但不要与它的["变体索引"](#variant-idx)混淆)。在运行时,激活变体的判别值被编码在[tag](#tag)中。
<span id="double-ptr">double pointer</span> | <span id="double-ptr">双指针</span> | 一个带有额外元数据的指针。同指「胖指针」。
<span id="drop-glue">drop glue</span> | <span id="drop-glue">drop胶水</span> | (内部)编译器生成的指令,处理调用数据类型的析构器(`Drop`)。
<span id="dst">DST</span> | <span id="dst">DST</span> | Dynamically-Sized Type的缩写,这是一种编译器无法静态知道内存大小的类型(例如:`str'或`[u8]`)。这种类型没有实现`Sized`,不能在栈中分配。它们只能作为结构中的最后一个字段出现。它们只能在指针后面使用(例如:`&str`或`&[u8]`)。
<span id="ebl">early-bound lifetime</span> | <span id="ebl">早绑定生存期</span> | 一个在其定义处被替换的生存期区域(region)。绑定在一个项目的`Generics'中,并使用`Substs'进行替换。与**late-bound lifetime**形成对比。
<span id="empty-type">empty type</span> | <span id="empty-type">空类型</span> | 参考 "uninhabited type".
<span id="fat-ptr">fat pointer</span> |<span id="fat-ptr">胖指针</span> | 一个两字(word)的值,携带着一些值的地址,以及一些使用该值所需的进一步信息。Rust包括两种 "胖指针":对切片(slice)的引用和特质(trait)对象。对切片的引用带有切片的起始地址和它的长度。特质对象携带一个值的地址和一个指向适合该值的特质实现的指针。"胖指针 "也被称为 "宽指针",和 "双指针"。
<span id="free-var">free variable</span> | <span id="free-var">自由变量</span> | 自由变量 是指没有被绑定在表达式或术语中的变量;
<span id="generics">generics</span> | <span id="generics">泛型</span> | 通用类型参数集。
<span id="hir">HIR</span> | <span id="hir">高级中间语言</span> | 高级中间语言,通过对AST进行降级(lowering)和去糖(desugaring)而创建。
<span id="hir-id">HirId</span> | <span id="hir-id">HirId</span> | 通过结合“def-id”和 "intra-definition offset"来识别HIR中的一个特定节点。
<span id="hir-map">HIR map</span> |<span id="hir-map">HIR map</span> | 通过`tcx.hir()`访问的HIR Map,可以让你快速浏览HIR并在各种形式的标识符之间进行转换。
<span id="ice">ICE</span> | <span id="ice">ICE</span> | 内部编译器错误的简称,这是指编译器崩溃的情况。
<span id="ich">ICH</span> | <span id="ich">ICH</span> | 增量编译哈希值的简称,它们被用作HIR和crate metadata等的指纹,以检查是否有变化。这在增量编译中是很有用的,可以查看crate的一部分是否发生了变化,应该重新编译。
<span id="infcx">infcx</span> | <span id="infcx">类型推导上下文</span> | 类型推导上下文(`InferCtxt`)。
<span id="inf-var">inference variable</span> | <span id="inf-var">推导变量</span> | 在进行类型或区域推理时,"推导变量 "是一种特殊的类型/区域,代表你试图推理的内容。想想代数中的X。例如,如果我们试图推断一个程序中某个变量的类型,我们就创建一个推导变量来代表这个未知的类型。
<span id="intern">intern</span> |<span id="intern">intern</span> | intern是指存储某些经常使用的常量数据,如字符串,然后用一个标识符(如`符号')而不是数据本身来引用这些数据,以减少内存的使用和分配的次数。
<span id="intrinsic">intrinsic</span> | <span id="intrinsic">内部函数</span> | 内部函数是在编译器本身中实现的特殊功能,但向用户暴露(通常是不稳定的)。它们可以做神奇而危险的事情。
<span id="ir">IR</span> | <span id="ir">IR</span> | Intermediate Representation的简称,是编译器中的一个通用术语。在编译过程中,代码被从原始源码(ASCII文本)转换为各种IR。在Rust中,这些主要是HIR、MIR和LLVM IR。每种IR都适合于某些计算集。例如,MIR非常适用于借用检查器,LLVM IR非常适用于codegen,因为LLVM接受它。
<span id="irlo">IRLO</span> | <span id="irlo">IRLO</span> | `IRLO`或`irlo`有时被用作[internals.rust-lang.org](https://internals.rust-lang.org)的缩写。
<span id="item">item</span> | <span id="item">语法项</span> | 语言中的一种 "定义",如静态、常量、使用语句、模块、结构等。具体来说,这对应于 "item"类型。
<span id="lang-item">lang item</span> | <span id="lang-item">语言项</span> | 代表语言本身固有的概念的项目,如特殊的内置特质,如`同步`和`发送`;或代表操作的特质,如`添加`;或由编译器调用的函数。
<span id="lbl">late-bound lifetime</span> | <span id="lbl">晚绑定生存期</span> | 一个在其调用位置被替换的生存期区域。绑定在HRTB中,由编译器中的特定函数替代,如`liberate_late_bound_regions`。与**早绑定的生存期**形成对比。
<span id="local-crate">local crate</span> | <span id="local-crate">本地crate</span> | 目前正在编译的crate。这与 "上游crate"相反,后者指的是本地crate的依赖关系。
<span id="lto">LTO</span> | <span id="lto">LTO</span> | 链接时优化(Link-Time Optimizations)的简称,这是LLVM提供的一套优化,在最终二进制文件被链接之前进行。这些优化包括删除最终程序中从未使用的函数,例如。_[ThinLTO]_是LTO的一个变种,旨在提高可扩展性和效率,但可能牺牲了一些优化。
<span id="llvm">LLVM</span> | <span id="llvm">LLVM</span> | (实际上不是一个缩写 :P) 一个开源的编译器后端。它接受LLVM IR并输出本地二进制文件。然后,各种语言(例如Rust)可以实现一个编译器前端,输出LLVM IR,并使用LLVM编译到所有LLVM支持的平台。
<span id="memoization">memoization</span> | <span id="memoization">memoization</span> | 储存(纯)计算结果(如纯函数调用)的过程,以避免在未来重复计算。这通常是执行速度和内存使用之间的权衡。
<span id="mir">MIR</span> | <span id="mir">中级中间语言</span> | 在类型检查后创建的中级中间语言,供borrowck和codegen使用。
<span id="miri">miri</span> | <span id="miri">mir解释器</span> | MIR的一个解释器,用于常量计算。
<span id="mono">monomorphization</span> | <span id="mono">单态化</span> | 采取类型和函数的通用实现并将其与具体类型实例化的过程。例如,在代码中可能有`Vec<T>`,但在最终的可执行文件中,将为程序中使用的每个具体类型有一个`Vec`代码的副本(例如,`Vec<usize>`的副本,`Vec<MyStruct>`的副本,等等)。
<span id="normalize">normalize</span> |<span id="normalize">归一化</span> | 转换为更标准的形式的一般术语,但在rustc的情况下,通常指的是关联类型归一化。
<span id="newtype">newtype</span> | <span id="newtype">newtype</span> | 对其他类型的封装(例如,`struct Foo(T)`是`T`的一个 "新类型")。这在Rust中通常被用来为索引提供一个更强大的类型。
<span id="niche">niche</span> | <span id="niche">利基</span> | 一个类型的无效位模式*可用于*布局优化。有些类型不能有某些位模式。例如,"非零*"整数或引用"&T "不能用0比特串表示。这意味着编译器可以通过利用无效的 "利基值 "来进行布局优化。这方面的一个应用实例是[*Discriminant elision on `Option`-like enums*](https://rust-lang.github.io/unsafe-code-guidelines/layout/enums.html#discriminant-elision-on-option-like-enums),它允许使用一个类型的niche作为一个`enum`的["标签"](#tag),而不需要一个单独的字段。
<span id="nll">NLL</span> | <span id="nll">NLL</span> | 这是非词法作用域生存期的简称,它是对Rust的借用系统的扩展,使其基于控制流图。
<span id="node-id">node-id or NodeId</span> | <span id="node-id">node-id or NodeId</span> | 识别AST或HIR中特定节点的索引;逐渐被淘汰,被`HirId`取代。
<span id="obligation">obligation</span> | <span id="obligation">obligation</span> | 必须由特质系统证明的东西。
<span id="placeholder">placeholder</span> | <span id="placeholder">placeholder</span> | **注意:skolemization被placeholder废弃**一种处理围绕 "for-all "类型的子类型的方法(例如,`for<'a> fn(&'a u32)`),以及解决更高等级的trait边界(例如,`for<'a> T: Trait<'a>`)。
<span id="point">point</span> | <span id="point">point</span> | 在NLL分析中用来指代MIR中的某个特定位置;通常用来指代控制流图中的一个节点。
<span id="polymorphize">polymorphize</span> | <span id="polymorphize">多态化</span> | 一种避免不必要的单态化的优化。
<span id="projection">projection</span> | <span id="projection">投影</span> | 一个 "相对路径 "的一般术语,例如,`x.f`是一个 "字段投影",而`T::Item`是一个"关联类型投影"
<span id="pc">promoted constants</span> | <span id="pc">常量提升</span> | 从函数中提取的常量,并提升到静态范围
<span id="provider">provider</span> | <span id="provider">provider</span> | 执行查询的函数。
<span id="quantified">quantified</span> | <span id="quantified">量化</span> |在数学或逻辑学中,存在量词和普遍量词被用来提出诸如 "是否有任何类型的T是真的?"或 "这对所有类型的T都是真的吗?"这样的问题
<span id="query">query</span> | <span id="query">查询</span> | 编译过程中的一个子计算。查询结果可以缓存在当前会话中,也可以缓存到磁盘上,用于增量编译。
<span id="recovery">recovery</span> | <span id="recovery">恢复</span> | 恢复是指在解析过程中处理无效的语法(例如,缺少逗号),并继续解析AST。这可以避免向用户显示虚假的错误(例如,当结构定义包含错误时,显示 "缺少字段 "的错误)。
<span id="region">region</span> | <span id="region">区域</span> | 和生存期精彩使用的另一个术语。
<span id="rib">rib</span> | <span id="rib">rib</span> | 名称解析器中的一个数据结构,用于跟踪名称的单一范围。
<span id="scrutinee">scrutinee</div> | <span id="scrutinee">审查对象</div> | 审查对象是在`match`表达式和类似模式匹配结构中被匹配的表达式。例如,在`match x { A => 1, B => 2 }`中,表达式`x`是被审查者。
<span id="sess">sess</span> | <span id="sess">sess</span> | 编译器会话,它存储了整个编译过程中使用的全局数据
<span id="side-tables">side tables</span> | <span id="side-tables">side tables</span> | 由于AST和HIR一旦创建就不可改变,我们经常以哈希表的形式携带关于它们的额外信息,并以特定节点的ID为索引。
<span id="sigil">sigil</span> | <span id="sigil">符号</span> | 就像一个关键词,但完全由非字母数字的标记组成。例如,`&`是引用的标志。
<span id="soundness">soundness</span> | <span id="soundness">健全性</span> | 类型理论中的一个技术术语。粗略的说,如果一个类型系统是健全的,那么一个进行类型检查的程序就是类型安全的。也就是说,人们永远不可能(在安全的Rust中)把一个值强加到一个错误类型的变量中。
<span id="span">span</span> | <span id="span">span</span> | 用户的源代码中的一个位置,主要用于错误报告。这就像一个文件名/行号/列的立体元组:它们携带一个开始/结束点,也跟踪宏的扩展和编译器去糖。所有这些都被装在几个字节里(实际上,它是一个表的索引)。
<span id="substs">substs</span> | <span id="substs">替换</span> | 给定的通用类型或项目的替换(例如,`HashMap<i32, u32>`中的`i32'、`u32')。
<span id="sysroot">sysroot</span> | <span id="sysroot">sysroot</span> | 用于编译器在运行时加载的构建工件的目录。
<span id="tag">tag</span> | <span id="tag">tag</span> | 枚举/生成器的 "标签 "编码激活变体/状态的判别式(discriminant)。 标签可以是 "直接的"(简单地将判别式存储在一个字段中)或使用"利基"。
<span id="tcx">tcx</span> | <span id="tcx">tcx</span> | "类型化上下文"(`TyCtxt`),编译器的主要数据结构。
<span id="lifetime-tcx">'tcx</span> | <span id="lifetime-tcx">'tcx</span> | `TyCtxt'所使用的分配区域的生存期。在编译过程中,大多数数据都会使用这个生存期,但HIR数据除外,它使用`'hir`生存期。
<span id="token">token</span> | <span id="token">词条</span> | 解析的最小单位。词条是在词法运算后产生的
<span id="tls">TLS</span> | <span id="tls">TLS</span> | 线程本地存储。变量可以被定义为每个线程都有自己的副本(而不是所有线程都共享该变量)。这与LLVM有一些相互作用。并非所有平台都支持TLS。
<span id="trait-ref">trait reference</span> | <span id="trait-ref">trait 引用</span> | 一个特质的名称,以及一组合适的输入类型/生存期。
<span id="trans">trans</span> | <span id="trans">trans</span> | 是 "转译"的简称,是将MIR转译成LLVM IR的代码。已经重命名为codegen。
<span id="ty">Ty</span> | <span id="ty">Ty</span> | 一个类型的内部表示。
<span id="tyctxt">TyCtxt</span> | <span id="tyctxt">TyCtxt</span> | 在代码中经常被称为tcx的数据结构,它提供对会话数据和查询系统的访问。
<span id="ufcs">UFCS</span> | <span id="ufcs">UFCS</span> | 通用函数调用语法(Universal Function Call Syntax)的简称,这是一种调用方法的明确语法。
<span id="ut">uninhabited type</span> | <span id="ut">孤类型</span> | 一个没有值的类型。这与ZST不同,ZST正好有一个值。一个孤类型的例子是`enum Foo {}`,它没有变体,所以,永远不能被创建。编译器可以将处理孤类型的代码视为死代码,因为没有这样的值可以操作。`!`(从未出现过的类型)是一个孤类型。孤类型也被称为 "空类型"。
<span id="upvar">upvar</span> | <span id="upvar">upvar</span> | 一个闭合体从闭合体外部捕获的变量
<span id="variance">variance</span> | <span id="variance">型变</span> | 确定通用类型/寿命参数的变化如何影响子类型;例如,如果`T`是`U`的子类型,那么`Vec<T>`是`Vec<U>`的子类型,因为`Vec`在其通用参数中是协变的。
<span id="variant-idx">variant index</span> |<span id="variant-idx">变体索引</span> | 在一个枚举中,通过给它们分配从0开始的索引来识别一个变体。这纯粹是内部的,不要与"判别式"相混淆,后者可以被用户覆盖(例如,`enum Bool { True = 42, False = 0 }`)。
<span id="wide-ptr">wide pointer</span> |<span id="wide-ptr">宽指针</span> |一个带有额外元数据的指针。
<span id="zst">ZST</span> | <span id="zst">ZST</span> | 零大小类型。这种类型,其值的大小为0字节。由于`2^0 = 1`,这种类型正好有一个值。例如,`()`(单位)是一个ZST。`struct Foo;`也是一个ZST。编译器可以围绕ZST做一些很好的优化。
[LLVM]: https://llvm.org/
[LTO]: https://llvm.org/docs/LinkTimeOptimization.html
[ThinLTO]: https://clang.llvm.org/docs/ThinLTO.html
[TLS]: https://llvm.org/docs/LangRef.html#thread-local-storage-models
**参考**
[Rust 编译器内部术语中英文对照表](https://rustcrustc.github.io/rustc-dev-guide-zh/appendix/glossary.html)
================================================
FILE: src/safe-guides/Appendix/test/benchmark.md
================================================
# 基准测试
说明: 借用 MogoDB 工程师 Patrick 的文章来了解 Rust 里做基准测试基本姿势。
> 原文: [https://patrickfreed.github.io/rust/2021/10/15/making-slow-rust-code-fast.html](https://patrickfreed.github.io/rust/2021/10/15/making-slow-rust-code-fast.html)
>
## 使用 Criterion.rs 和 火焰图(flamegraphs) 进行性能调优
性能是开发者为其应用程序选择 Rust 的首要原因之一。事实上,它是 `rust-lang.org` 主页上 ["为什么选择Rust?"](https://www.rust-lang.org/#:~:text=Version%201.55.0-,Why%20Rust%3F,-Performance)一节中列出的第一个原因,甚至在内存安全之前。这也是有原因的,许多基准测试表明,用Rust编写的软件速度很快,有时甚至是[最快](https://www.techempower.com/benchmarks/#section=data-r18&hw=ph&test=fortune)的。但这并不意味着所有用Rust编写的软件都能保证快速。事实上,写低性能的Rust代码是很容易的,特别是当试图通过Clone 或`Arc`替代借用来""安抚""借用检查器时,这种策略通常被推荐给 Rust 新手。这就是为什么对 Rust 代码进行剖析和基准测试是很重要的,可以看到任何瓶颈在哪里,并修复它们,就像在其他语言中那样。在这篇文章中,我将根据最近的工作经验,展示一些基本的工具和技术,以提高 `mongodb` crate 的性能。
注意:本帖中使用的所有示例代码都可以在[这里](https://github.com/patrickfreed/benchmark-example)找到。
## 索引
- [性能剖析(Profiling)](#性能剖析)
- [基准测试(Benchmarking)](#基准测试)
- [火焰图生成](#火焰图生成)
- [识别火焰图中的瓶颈](#识别火焰图中的瓶颈)
- [ `Clone` 的 “袭击”](#`Clone`的“袭击”)
- [加速反序列化](#加速反序列化)
- [分析结果](#分析结果)
- [查看Criterion的HTML报告](查看Criterion的HTML报告)
- [使用`wrk`进行压测](#使用`wrk`进行压测)
- [下一步](#下一步)
- [总结](#总结)
- [广告时间](#广告时间)
## 性能剖析
在进行任何性能调优工作时,在试图修复任何东西之前,绝对有必要对代码进行性能剖析(profiling),因为瓶颈往往位于意想不到的地方,而且怀疑的瓶颈往往不如你想的那样对性能有足够影响。如果不遵守这一原则,就会导致[过早优化](https://wiki.c2.com/?PrematureOptimization),这可能会不必要地使代码复杂化并浪费开发时间。这也是为什么建议新人在开始的时候自由地 Clone ,这样可以帮助提高可读性,而且可能不会对性能产生严重的影响,但是如果他们这样做了,以后的性能剖析会发现这一点,所以在那之前没有必要担心。
> 过早优化(Premature Optimization)
>
> Premature optimization is the root of all evil. -- DonaldKnuth
>
> 在 DonaldKnuth 的论文 《 Structured Programming With GoTo Statements 》中,他写道:"程序员浪费了大量的时间去考虑或担心程序中非关键部分的速度,而当考虑到调试和维护时,这些对效率的尝试实际上会产生强烈的负面影响。我们应该忘记这种微小的效率,比如说因为过早优化而浪费的大约97%的时间。然而,我们不应该放弃那关键的 3% 的机会"。
### 基准测试
剖析的第一步是建立一套一致的基准,可以用来确定性能的基线水平,并衡量任何渐进的改进。在 `mongodb` 的案例中,标准化的[`MongoDB` 驱动微基准集](https://github.com/mongodb/specifications/blob/master/source/benchmarking/benchmarking.rst)在这方面发挥了很好的作用,特别是因为它允许在用其他编程语言编写的`MongoDB`驱动之间进行比较。由于这些是 "微 "基准,它们还可以很容易地测量单个组件的变化(例如,读与写),这在专注于在特定领域进行改进时是非常有用的。
一旦选择了基准,就应该建立一个稳定的环境,可以用来进行所有的定时测量。确保环境不发生变化,并且在分析时不做其他 "工作"(如浏览猫的图片),这对减少基准测量中的噪音很重要。
#### 用 `cargo bench` 和 `Criterion.rs` 来执行基准测试
Rust 提供的基准测试只能在 Nightly 下使用,因为它还未稳定。它对简单的基准测试比较有用,但是功能有限,而且没有很好的文档。另一个选择是[ `criterion`](https://crates.io/crates/criterion) crate。它为基准测试提供了更多的可配置性和丰富的功能支持,同时支持稳定的Rust !我将详细介绍基本的 criterion crate。
我将在这里详细介绍一个基本的 criterion 设置,但如果想了解更多信息,我强烈推荐你查看优秀的[ Criterion.rs 用户指南](https://bheisler.github.io/criterion.rs/book/index.html)。
在对`mongodb`进行基准测试时,我首先使用`cargo new <my-benchmark-project>`创建了一个新项目,并在`Cargo.toml`中添加了以下几行。
```toml
[dependencies]
tokio = { version = "1", features = ["full"] }
futures = { version = "0.3", default-features = false }
mongodb = { path = "/home/patrick/mongo-rust-driver" }
[dev-dependencies]
criterion = { version = "0.3.5", features = ["async_tokio", "html_reports"] }
[[bench]]
name = "find"
harness = false
```
在我的基准测试中,使用了` tokio` 异步运行时,所以我需要把它指定为一个依赖项,并启用`async_tokio`的 `criterion ` features,但如果你不使用`tokio`,这不是必需的。我还需要使用`futures` crate提供的一些功能,但这对于运行一个`criterion` 基准来说也是没有必要的。对于我的`mongodb`依赖,我指定了一个本地克隆库的路径,这样我就可以对我做的任何改动进行基准测试。另外,在这个例子中,我将专注于对`mongodb` crate的[`Collection::find`](https://docs.rs/mongodb/2.0.0/mongodb/struct.Collection.html#method.find)方法进行基准测试,所以我对基准进行了相应的命名,但你可以对你的基准测试进行任意命名。
接下来,需要创建一个`benches/find.rs`文件来包含基准测试。文件名需要与`Cargo.toml`中的名称字段中指定的值相匹配。下面是一个测试`Collection::find`性能的简单基准测试的例子。
```rust
use criterion::{criterion_group, criterion_main, Criterion};
use futures::TryStreamExt;
use mongodb::{
bson::{doc, Document},
Client,
};
pub fn find_bench(c: &mut Criterion) {
// begin setup
// create the tokio runtime to be used for the benchmarks
let rt = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap();
// seed the data server side, get a handle to the collection
let collection = rt.block_on(async {
let client = Client::with_uri_str("mongodb://localhost:27017")
.await
.unwrap();
let collection = client.database("foo").collection("bar");
collection.drop(None).await.unwrap();
let doc = doc! {
"hello": "world",
"anotherKey": "anotherValue",
"number": 1234
};
let docs = vec![&doc; 10_000];
collection.insert_many(docs, None).await.unwrap();
collection
});
// end setup
c.bench_function("find", |b| {
b.to_async(&rt).iter(|| {
// begin measured portion of benchmark
async {
collection
.find(doc! {}, None)
.await
.unwrap()
.try_collect::<Vec<Document>>()
.await
.unwrap();
}
})
});
}
criterion_group!(benches, find_bench);
criterion_main!(benches);
```
`find_bench`函数包含设置和运行基准的所有代码。该函数可以被任意命名,但是它需要接收一个`&mut Criterion`作为参数。该函数的第一部分包含设置代码,在基准运行前只执行一次,其运行时间根本不被测量。实际测量的部分是稍后被传入`Bencher::iter`的闭包。该闭包将被多次运行,每次运行的时间将被记录、分析,并包含在一个HTML报告中。
在这个特定的例子中,设置涉及到创建`tokio`运行时,该运行时将用于基准测试的其余部分。通常,这是在幕后通过`tokio::main`宏完成的,或者,在库的情况下,根本就不需要。然而,我们需要在这里手动创建一个运行时,以便我们以后可以通过`Bencher::to_async`方法将其传递给`criterion`。一旦运行时被创建,设置就会继续进行,即填充我们在实际基准中要查询的`MongoDB`集合。由于这涉及到异步`API`的使用,我们需要通过`Runtime::block_on`确保它们在异步运行时的上下文中执行。在实际测量部分,我们对设置时创建的集合中的所有文档进行查询。
所有这些都准备好了(并且我们的`MongoDB`实例正在运行),我们可以运行`cargo bench`来建立我们的基线。输出结果将如下。
```rust
~/benchmark-example$ cargo bench
Finished bench [optimized] target(s) in 0.07s
Running unittests (target/release/deps/benchmark_example-b9c25fd0639c5e9c)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running unittests (target/release/deps/find-e1f66bfc9cf31158)
Gnuplot not found, using plotters backend
Benchmarking find: Warming up for 3.0000 s
find time: [55.442 ms 55.663 ms 55.884 ms]
```
这里最重要的信息是时间: `[55.442 ms 55.663 ms 55.884 ms]`。中间的值是对每次迭代所花时间的最佳估计,第一个和最后一个值定义了置信区间(Confidence interval)的上界和下界。默认情况下,使用的置信度是`95%`,这意味着该区间有`95%`的机会包含迭代的实际平均运行时间。关于这些值以及如何计算的更多信息,请查看`Criterion.rs`用户指南。
现在,如果我们再次执行`cargo bench`,它将记录更多的时间,并与之前的时间进行比较(之前的数据存储在目标/标准中),报告任何变化。鉴于我们根本没有改变代码,这应该报告说没有任何变化。
```rust
find time: [55.905 ms 56.127 ms 56.397 ms]
change: [+0.3049% +0.8337% +1.4904%] (p = 0.01 < 0.05)
Change within noise threshold.
Found 5 outliers among 100 measurements (5.00%)
1 (1.00%) low mild
2 (2.00%) high mild
2 (2.00%) high severe
```
正如预期的那样,criterion 报告说,与上次运行相比,任何性能的变化都可能是由于噪音造成的。现在我们已经建立了一个基线,现在是时候对代码进行剖析,看看它哪里慢。
### 火焰图生成
[perf](https://perf.wiki.kernel.org/index.php/Main_Page) 是一个Linux命令行工具,可以用来获取一个应用程序的性能信息。我们不会直接使用它,而是通过[`flamegraph` ](https://crates.io/crates/flamegraph) crate,它是一个基于Rust的flamegraph生成器,可以与`cargo`一起工作。
火焰图([`Flamegraphs`](https://github.com/brendangregg/FlameGraph))是程序在每个函数中花费时间的有用的可视化数据。在被测量的执行过程中调用的每个函数被表示为一个矩形,每个调用栈被表示为一个矩形栈。一个给定的矩形的宽度与在该函数中花费的时间成正比,更宽的矩形意味着更多的时间。火焰图对于识别程序中的慢速部分非常有用,因为它们可以让你快速识别代码库中哪些部分花费的时间不成比例。
要使用`cargo`生成`flamegraphs`,首先我们需要安装`perf`和`flamegraph `crate。这在`Ubuntu`上可以通过以下方式完成。
```rust
sudo apt-get install linux-tools-common linux-tools-`uname -r`
cargo install flamegraph
```
一旦安装完成,我们就可以生成我们的基线的第一个`flamegraph`! 要做到这一点,请运行以下程序。
```rust
cargo flamegraph --bench find -o find-baseline.svg -- --bench
```
然后你可以在浏览器中打开`find-baseline.svg`来查看火焰图。如果你在运行`cargo flamegraph`时遇到权限问题,请参阅`flamegraph` crate的`README`中的说明。
生成 criterion 基准的`flamegraph`可能会有噪音,因为很多时间都花在了 criterion(例如测量时间)和设置上,而不是在被基准测试的部分。为了减少火焰图中的一些噪音,你可以写一个与基准的测量部分行为类似的程序,然后生成另一个火焰图来代替。
例如,我用下面的命令从一个普通的二进制程序中生成一个火焰图,该程序使用我的本地`mongodb` crate副本来执行没有criterion的查找。
```
cargo flamegraph --bin my-binary -o find-baseline.svg
```
这里是生成的火焰图(在新的浏览器标签页中打开它来探索)。

现在我们可以看到时间花在哪里了,现在是时候深入研究,看看我们是否能找到瓶颈。
### 识别火焰图中的瓶颈
火焰图中的栈从底部开始,随着调用栈的加深而向上移动(左右无所谓),通常这是开始阅读它们的最佳方式。看一下上面火焰图的底部,最宽的矩形是`Future::poll`,但这并不是因为Rust 的 `Future` 超级慢,而是因为每个`.await`都涉及轮询(poll)`Future`。考虑到这一点,我们可以跳过任何轮询矩形,直到我们可以在`mongodb`中看到我们关心的信息的函数。下面火焰图的注释版本,突出了需要注意的部分。

蓝色方块包含了调用`CommandResponse::body`所花费的时间,它显示几乎所有的时间都花在了`clone()`上。各个紫色矩形对应的是将`BSON`(MongoDB使用的二进制格式)解析到`Document`中所花费的时间,绿色矩形对应的是`Document`的`serde::Deserialize`实现中所花费的时间。最后,黑色虚线矩形对应的是释放内存的时间,黑色实线对应的是将命令序列化为`BSON`的时间。
现在我们知道了大部分时间花在哪里(只在少数几个地方),我们可以集中精力实际改变代码,使其更快。
## `Clone`的“袭击”
无论做任何事,从最容易实现的地方开始,往往可以产生最好的回报。在这个例子中,只是 `clone` 就花费了一大块时间,所以我们能简单地消除 `clone`。从火焰图里知道,最昂贵的`clone` 就是 `CommandResponse::body` 中调用的那个,所以我们去看看这个[方法](https://github.com/mongodb/mongo-rust-driver/blob/v2.0.0-beta/src/cmap/conn/command.rs#L138)。
在 `command.rs:149` 行,我们看到如下定义:
```rust
/// Deserialize the body of the response.
pub(crate) fn body<T: DeserializeOwned>(&self) -> Result<T> {
match bson::from_bson(Bson::Document(self.raw_response.clone())) {
Ok(body) => Ok(body),
Err(e) => Err(ErrorKind::ResponseError {
message: format!("{}", e),
}
.into()),
}
}
```
我们可以看到,这里确实有一个对`clone`的调用,所以它很可能是我们在火焰图中看到的耗费大量时间的那个。`clone`是必须的,因为我们需要从`self`所拥有的`raw_response`中反序列化,但我们只有对`self`的引用,所以我们不能从其中移出(move out)。我们也不能通过引用来使用`raw_response`,因为`bson::from_bson`期望一个有所有权的值。让我们研究一下 `body` 本身被调用的地方,看看我们是否可以改变它以获得 `self `的所有权,从而避免`clone`。
具体来看这个基准的使用情况,在`Find::handle_response`中,查找操作使用它来反序列化服务端上的`response`。
```rust
fn handle_response(&self, response: CommandResponse) -> Result<Self::O> { let body: CursorBody = response.body()?; Ok(CursorSpecification::new( self.ns.clone(), response.source_address().clone(), body.cursor.id, self.options.as_ref().and_then(|opts| opts.batch_size), self.options.as_ref().and_then(|opts| opts.max_await_time), body.cursor.first_batch, ))}
```
正如我们在这里看到的,`response`只在调用` body `后使用了一次,而且这一次的使用可以在它之前没有问题,所以如果 `body` 取得了` self `的所有权,这个调用点至少还能工作。对其余的调用点重复这个过程,我们看到`body`实际上可以取得`self`的所有权,从而避免`clone`,所以让我们做这个改变,看看它对性能有什么影响。
在做了这个改变之后,重新运行`cargo bench`的结果如下。
```rust
find time: [47.495 ms 47.843 ms 48.279 ms] change: [-15.488% -14.760% -13.944%] (p = 0.00 < 0.05) Performance has improved.Found 4 outliers among 100 measurements (4.00%) 4 (4.00%) high severe
```
很好! 即使在这样一个简单的改变之后,我们已经观察到了性能上的明显改善。既然一些简单的问题已经被解决了,让我们调查一下其他花费大量时间的地方。
## 加速反序列化
回顾一下火焰图,我们可以看到很大一部分时间都花在了解析来自 `MongoDB Wire` 协议(紫色)的响应上,然后通过`serde`(绿色)将它们反序列化为 Rust 数据结构。尽管每一个步骤都在执行类似的任务,但这两个步骤是需要的,因为`bson` crate只支持从`Bson`和`Document` Rust类型反序列化,而不是实际的`BSON`,即`MongoDB wire` 协议中使用的二进制格式。火焰图表明,这个过程消耗了大量的时间,因此如果这两个步骤可以合并为一个,有可能会带来显著的性能优势。
本质上,我们想从以下几个方面入手。
```rust
let bytes = socket.read(&mut bytes).await?; // read message from databaselet document = Document::from_reader(bytes.as_slice())?; // parse into Documentlet rust_data_type: MyType = bson::from_document(document)?; // deserialize via serde
```
合并为:
```rust
let bytes = socket.read(&mut bytes).await?; // read message from databaselet rust_data_type: MyType = bson::from_slice(bytes.as_slice())?; // deserialize via serde
```
要做到这一点,我们需要实现一个新的`serde` 的 `Deserializer`,它可以与原始`BSON`一起工作。这方面的工作相当广泛,而且相当复杂,所以我就不说细节了。`serde`文档中的 " [实现 Deserializer](https://serde.rs/impl-deserializer.html) "部分为那些感兴趣的人提供了一个实现`JSON`的优秀例子。
那么,现在我们[实现了 Deserializer](https://github.com/mongodb/bson-rust/commit/7ccf82b3dc66141d8292a5c1e253362abaa13d5c)并 [更新了驱动程序](https://github.com/mongodb/mongo-rust-driver/commit/0fa2a905c1f5411a6f0109debe18c3cfa35c94be) 以使用它,让我们重新运行`cargo bench`,看看它是否对性能有任何影响。
```rust
find time: [30.624 ms 30.719 ms 30.822 ms] change: [-36.409% -35.791% -35.263%] (p = 0.00 < 0.05) Performance has improved.Found 5 outliers among 100 measurements (5.00%) 1 (1.00%) low mild 1 (1.00%) high mild 3 (3.00%) high severe
```
棒极了! 平均迭代时间比上一次大约减少了36%,这与最初的基线相比已经有了很大的减少。现在我们已经实施了一些改进,让我们仔细看看结果。
## 分析结果
### 查看Criterion的HTML报告
`Criterion`支持生成一个`HTML`报告,总结最近的运行情况,并与之前的运行情况进行比较。要访问该报告,只需在浏览器中打开`target/criterion/report/index.html`。
作为一个例子,[这里](https://patrickfreed.github.io/resources/making-slow-rust-code-fast/criterion/find/report/index.html)是比较基线和最优化的报告。
在报告的顶部,我们可以看到最优化运行的总结,包括一个说明平均执行时间的图表和一个显示所有样本标准的散点图,以及一些其他图表的链接。下面是最近一次查找基准运行的该部分的屏幕截图。

在报告的底部,有一个最近两次运行的比较,较旧的运行(基线)为红色,较新的运行(优化后的)为蓝色。下面是优化后的`mongodb`版本与未优化的基线比较的部分的截图。在其中,我们可以看到,未优化的基线显然要比优化的慢得多。从分布的广度来看,我们也可以看到,优化版的性能比基线版的更稳定。

这些报告是超级有用的工具,可以直观地看到因性能调优而发生的变化,而且对于向他人介绍结果特别有用。它们还可以作为过去性能数据的记录,消除了手动记录结果的需要。
### 使用`wrk`进行压测
虽然微基准对隔离行为和识别瓶颈非常有用,但它们并不总是代表真实的工作负载。为了证明所做的改变确实提高了性能,并且没有过度适应微基准,在真实世界的场景中进行测量也是很有用的。
对于像`mongodb`这样的异步数据库驱动来说,这意味着有大量并发请求的情况。一个生成这种请求的有用工具是`wrk`工作负载生成器。
要安装`wrk`,你需要`clone repo`并从源代码中构建它。
```rust
git clone https://github.com/wg/wrkcd wrkmake./wrk --version
```
如果成功了,你应该看到`wrk`的版本信息。关于更具体的安装说明,请看 `wrk` 的 `INSTALL` 页面。
在启动了一个`actix-web`服务器(在release 模式下运行),它将对每个`GET`请求执行查找,我用下面的调用将`wrk`指向它。
```
./wrk -t8 -c100 -d10s http://127.0.0.1:8080
```
这将在`10`秒内运行一个基准,使用`8`个线程,并保持`100`个HTTP连接开放。
使用未经优化的驱动程序,我看到了以下结果。
```
Running 10s test @ http://127.0.0.1:8080 8 threads and 100 connections Thread Stats Avg Stdev Max +/- Stdev Latency 7.83ms 2.06ms 26.52ms 73.81% Req/Sec 1.54k 379.64 7.65k 91.02% 122890 requests in 10.10s, 205.45MB readRequests/sec: 12168.39Transfer/sec: 20.34MB
```
优化后,我看到的却是这样的结果。
```
Running 10s test @ http://127.0.0.1:8080 8 threads and 100 connections Thread Stats Avg Stdev Max +/- Stdev Latency 4.03ms 1.31ms 52.06ms 97.77% Req/Sec 3.03k 292.52 6.00k 92.41% 242033 requests in 10.10s, 404.63MB readRequests/sec: 23964.39Transfer/sec: 40.06MB
```
.这意味着吞吐量几乎增加了`100%`,真棒!这意味着我们基于微基准的优化对实际工作负载有非常显著改善。
## 下一步
在这篇文章中,我们已经看到了如何只用一些基本的性能技术(生成火焰图、基准测试)就能在你的Rust应用程序中实现显著的性能改进。这方面的过程可以总结为以下步骤。
1. 使用 `criterion ` 运行一个基准,以建立一个基线
2. 通过`cargo flamegraph`识别瓶颈
3. 尝试解决瓶颈问题
4. 重新运行基准测试,看看瓶颈是否得到解决
5. 重复进行以上步骤
这个过程可以反复进行,直到达到一个令人满意的性能水平。然而,随着你的迭代,改进可能会变得不那么显著,需要更多的努力来实现。例如,在`mongodb`的例子中,第一个大的改进来自于更明智地使用`clone()`,但为了达到类似的改进水平,需要实现整个`serde` 的 `Deserializer` 。这就引出了性能剖析如此重要的另一个原因:除了识别需要优化的地方外,它还可以帮助确定何时需要优化(或者反过来说,何时应该停止优化)。如果剩下的改进不值得努力,性能剖析可以表明这一点,让你把精力集中在其他地方。这一点很重要,因为无论某件事情如何优化,总是有改进的余地,而且很容易陷入过度优化的无底洞中。
## 总结
我希望这个关于 Rust 中性能剖析和基准测试的概述是有帮助的。请注意,将你的 Rust 应用程序或库,优化到技术上尽可能快,并不总是必须的。因为优化的代码往往比简单但缓慢的代码更难理解和维护。
更重要的是,你的应用程序或库要满足其性能预期。例如,如果一个`CLI`工具的自我更新需要`50`毫秒或`100`毫秒,尽管有可能减少`50%`的运行时间,这并没有什么区别,因为`100`毫秒完全在这种功能的预期性能水平之内。然而,对于那些性能没有达到预期的情况,这篇文章中所概述的过程可以非常有效地产生优化,正如我们最近对`mongodb `crate所做的改进中所看到的。
## 广告时间
我们最近发布了[`mongodb`](https://crates.io/crates/mongodb) crate的[ `v2.0.0`](https://github.com/mongodb/mongo-rust-driver/releases/tag/v2.0.0)版本,其中包含了这篇文章中提到的性能改进,以及大量的新功能,包括对事务的支持。如果你对用Rust编写Web应用程序感兴趣,如果你需要一个数据库,请查看`MongoDB` Rus t驱动。
##
================================================
FILE: src/safe-guides/Appendix/test/fuzz.md
================================================
# 模糊测试
[模糊测试(Fuzz testing)](https://en.wikipedia.org/wiki/Fuzz_testing)是一种软件测试技术,用于通过向软件提供伪随机数据作为输入来发现安全性和稳定性问题。
关于模糊测试可以参考:
- [Rust Fuzz Book](https://rust-fuzz.github.io/book/introduction.html)
- [https://github.com/rust-fuzz](https://github.com/rust-fuzz)
- [How to fuzz Rust code continuously](https://about.gitlab.com/blog/2020/12/03/how-to-fuzz-rust-code/)
================================================
FILE: src/safe-guides/Appendix/test/unit_test.md
================================================
# 单元测试
Rust 支持单元测试。
## 测试代码组织
对于内部函数,单元测试代码最好放到业务代码的同一个模块下。
对于外部接口,单元测试最好放到独立的 `tests` 目录。
## 文档测试
对所有对外接口进行文档测试是一个不错的开始。
## 编译测试
通过 `compiletest` 来测试某些代码可能无法编译。 参考: [Rustc开发指南](https://rustc-dev-guide.rust-lang.org/tests/adding.html#ui)
## 随机测试
使用 第三方库`proptest` 来进行随机测试。
```rust
use proptest::prelude::*;
proptest! {
#[test]
fn check_count_correct(haystack: Vec<u8>, needle: u8) {
prop_assert_eq!(count(&haystack, needle), naive_count(&haystack, needle));
}
}
```
## 代码测试率覆盖检测工具
[tarpaulin](https://github.com/xd009642/tarpaulin) 是 Cargo 构建系统的代码覆盖率报告工具,目前 **仅支持运行 Linux 的 x86_64 处理器**。
================================================
FILE: src/safe-guides/Appendix/test.md
================================================
# B.测试
================================================
FILE: src/safe-guides/Appendix/toc.md
================================================
# 附录
- [A.开发环境](./dev_env.md)
- [B.测试](./test.md)
- [单元测试](./test/unit_test.md)
- [基准测试](./test/benchmark.md)
- [模糊测试](./test/fuzz.md)
- [C.术语解释](./terms.md)
- [D.模板](./templates/intro.md)
- [rustfmt 模板](./templates/rustfmt.toml.md)
- [clippy 模板](./templates/clippy.toml.md)
- [deny 模板](./templates/deny.toml.md)
- [E.工具链](./tools/intro.md)
- [rustfmt](./tools/rustfmt.md)
- [noisy-clippy](./tools/noisy-clippy.md)
- [cargo-udeps](./tools/cargo-udeps.md)
- [F.Cheat Sheet](./cheat-sheet/README.md)
- [浮点数](./cheat-sheet/Numbers/float.md)
- [G.优化指南](./optimizing/intro.md)
- [H.编译参数说明](./rustc-flag.md)
- [I.最佳实践](./best-practice/intro.md)
- [初学者常见问题Q&A](./best-practice/qa.md)
- [Rust 编程技巧](./best-practice/tips.md)
- [J.贡献说明](./contribution.md)
- [K.淘汰的规则](./old_guidelines.md)
================================================
FILE: src/safe-guides/Appendix/tools/cargo-udeps.md
================================================
# Cargo Udeps
[cargo-udeps](https://github.com/est31/cargo-udeps) 检查 `Cargo.toml` 中未使用的依赖。
`cargo udeps` 对标的是` rustc` 的` unused_crate_dependencies lint`
虽然 rustc 也能检查一些未使用依赖,但是在 lib 和 bin 混合的项目中误报率高
```
RUSTFLAGS="-Dunused_crate_dependencies" cargo c
```
`cargo udeps` 的最大优点就是**几乎没有误报**。
但是检查力度不如` rustc unused_crate_dependencies lint `仔细,建议二者搭配使用
================================================
FILE: src/safe-guides/Appendix/tools/intro.md
================================================
# E.工具链
这里介绍一些检测工具,比如 Cargo fmt 和 Cargo Clippy.
## 参考资料
1. [https://doc.rust-lang.org/rustc/lints/groups.html](https://doc.rust-lang.org/rustc/lints/groups.html)
2. [https://rust-lang.github.io/rust-clippy/master/index.html](https://rust-lang.github.io/rust-clippy/master/index.html)
3. [https://rust-lang.github.io/rust-clippy/master/index.html](https://rust-lang.github.io/rust-clippy/master/index.html)
4. [Dtolnay 对 crates.io 中 clippy lint 应用统计](https://github.com/dtolnay/noisy-clippy)
================================================
FILE: src/safe-guides/Appendix/tools/noisy-clippy.md
================================================
# 在 Rust 生态中被拒绝的一些默认开启的lint
来源:[https://github.com/dtolnay/noisy-clippy](https://github.com/dtolnay/noisy-clippy)
以下按字母顺序排列。
## `absurd_extreme_comparisons`
[https://rust-lang.github.io/rust-clippy/master/index.html#absurd_extreme_comparisons](https://rust-lang.github.io/rust-clippy/master/index.html#absurd_extreme_comparisons)
【描述】
默认为 `Deny`,但在实际应用中,多被设置为 `allow`。
## `blacklisted_name`
[https://rust-lang.github.io/rust-clippy/master/index.html#blacklisted_name](https://rust-lang.github.io/rust-clippy/master/index.html#blacklisted_name)
【描述】
该 lint 不允许代码中出现 「内置黑名单」中定义的命名,比如 `foo`、`baz`。
默认为 `Warn`,但在实际应用中,可能被设置为`allow`,因为在某些样板代码、文档或测试代码中可能需要使用 `foo`。
## `blanket_clippy_restriction_lints`
[https://rust-lang.github.io/rust-clippy/master/index.html#blanket_clippy_restriction_lints](https://rust-lang.github.io/rust-clippy/master/index.html#blanket_clippy_restriction_lints)
【描述】
用于检查针对整个 `clippy::restriction` 类别的警告/拒绝/禁止属性。Restriction lint 有时与其他 lint 形成对比,甚至与惯用的 Rust 背道而驰。 这些 lint 应仅在逐个 lint 的基础上启用并仔细考虑。
默认为 `suspicious/warn`,但实际有些项目中会将其设置为 `allow`。
================================================
FILE: src/safe-guides/Appendix/tools/rustfmt.md
================================================
# Rustfmt 配置相关说明
## 在 Stable Rust 下使用未稳定配置项的方法
1. CI Job 可以分为 `Stable` 和 `Nightly`。在 `Stable CI` 下进行编译,在`Nightly CI`下执行`cargo fmt` 和 `cargo clippy`。
2. 在项目本地可以使用 `cargo +nightly fmt` 代替 `cargo fmt`。
注意: 一定要在文件保存之后再运行 rustfmt`,否则容易出错。
## 真实项目中的配置案例
1. 来自 [Rust 语言自身项目](https://github.com/rust-lang/rust/blob/master/rustfmt.toml)。
```toml
# Run rustfmt with this config (it should be picked up automatically).
version = "Two"
use_small_heuristics = "Max"
merge_derives = false
# by default we ignore everything in the repository
# tidy only checks files which are not ignored, each entry follows gitignore style
ignore = [
"/build/",
"/*-build/",
"/build-*/",
"/vendor/",
# tests for now are not formatted, as they are sometimes pretty-printing constrained
# (and generally rustfmt can move around comments in UI-testing incompatible ways)
"src/test",
# do not format submodules
"library/backtrace",
"library/stdarch",
"compiler/rustc_codegen_cranelift",
"src/doc/book",
"src/doc/edition-guide",
"src/doc/embedded-book",
"src/doc/nomicon",
"src/doc/reference",
"src/doc/rust-by-example",
"src/doc/rustc-dev-guide",
"src/llvm-project",
"src/tools/cargo",
"src/tools/clippy",
"src/tools/miri",
"src/tools/rls",
"src/tools/rust-analyzer",
"src/tools/rustfmt",
"src/tools/rust-installer",
]
```
1. 来自 [Google Fuchsia 操作系统](https://cs.opensource.google/fuchsia/fuchsia/+/main:rustfmt.toml)。
```toml
# Fuchsia Format Style
# last reviewed: Jan 29, 2019
# Fuchsia uses 2018 edition only
edition = "2018"
# The "Default" setting has a heuristic which splits lines too aggresively.
# We are willing to revisit this setting in future versions of rustfmt.
# Bugs:
# * https://github.com/rust-lang/rustfmt/issues/3119
# * https://github.com/rust-lang/rustfmt/issues/3120
use_small_heuristics = "Max"
# Prevent carriage returns
newline_style = "Unix"
```
2. 来自 [Tikv](https://github.com/tikv/tikv/blob/master/rustfmt.toml) 。
```toml
version = "Two"
unstable_features = true
condense_wildcard_suffixes = true
license_template_path = "etc/license.template"
newline_style = "Unix"
use_field_init_shorthand = true
use_try_shorthand = true
```
```toml
edition = "2018"
newline_style = "unix"
# comments
normalize_comments=true
wrap_comments=true
# imports
imports_granularity="Crate"
group_imports="StdExternalCrate"
```
## 一些全局配置项
### rustfml 格式化版本
【描述】
`Version::One` 向后兼容 Rustfmt 1.0。 其他版本仅在主要版本号内向后兼容。目前 `version` 可选值只有 `One` 和 `Two`。
【对应配置项】
| 对应选项 | 可选值 | 是否 stable | 说明 |
| ------ | ---- | ---- | ---- |
| [`version`](https://rust-lang.github.io/rustfmt/?#version) | One(默认) | No| 指定 rustfmlt 格式化版本 |
【示例】
```toml
# Run rustfmt with this config (it should be picked up automatically).
version = "Two"
```
### 指定文件或目录跳过格式化
【描述】
跳过与指定模式匹配的格式化文件和目录。 模式格式与 `.gitignore` 相同。 一定要使用 `Unix/forwardslash/style` 路径,此路径样式适用于所有平台。 不支持带有反斜杠 `\` 的 Windows 样式路径。
【对应配置项】
| 对应选项 | 可选值 | 是否 stable | 说明 |
| ------ | ---- | ---- | ---- |
| [`ignore`](https://rust-lang.github.io/rustfmt/?#ignore) | 格式化每一个Rust文件(默认) | No| 指定文件或目录跳过格式化 |
【示例】
```rust
// 跳过指定文件
ignore = [
"src/types.rs",
"src/foo/bar.rs",
]
// 跳过指定目录
ignore = [
"examples",
]
// 跳过项目内所有文件
ignore = ["/"]
```
### 禁用格式化
【描述】
可以通过 `disable_all_formatting=true` 配置来禁用格式化。默认是开启的。
【对应配置项】
| 对应选项 | 可选值 | 是否 stable | 说明 |
| ------ | ---- | ---- | ---- |
| [`disable_all_formatting`](https://rust-lang.github.io/rustfmt/?#disable_all_formatting) | false(默认) | No| 禁止格式化 |
### 配置 edition 版次
【描述】
如果通过 Cargo 的格式化工具 cargo fmt 执行,Rustfmt 能够通过读取 Cargo.toml 文件来获取使用的版本。 否则,需要在配置文件中指定版本。
【对应配置项】
| 对应选项 | 可选值 | 是否 stable | 说明 |
| ------ | ---- | ---- | ---- |
| [`edition`](https://rust-lang.github.io/rustfmt/?#edition) | 2015(默认) | No| 配置 edition 版次 |
【示例】
```toml
edition = "2018"
```
### 开启未稳定特性
【描述】
默认未启用,但是可以通过配置此功能在 Nightly 上启用此功能。
【对应配置项】
| 对应选项 | 可选值 | 是否 stable | 说明 |
| ------ | ---- | ---- | ---- |
| [`unstable_features`](https://rust-lang.github.io/rustfmt/?#unstable_features) | false(默认) | No| 开启未稳定特性 |
## 每行最大宽度为 100 个字符
**【级别】** 建议
**【描述】**
代码行宽不宜过长,否则不利于阅读。
建议每行字符数不要超过 100 个字符。
`rustfmt` 还提供很多其他宽度设置:
- fn_call_width, 函数调用最大宽度设置,其默认值是 `max_width`的 `60%`。
- attr_fn_like_width, 像函数那样使用的属性宏最大宽度,其默认值是 `max_width`的 `70%`。
- struct_lit_width, 结构体字面量最大宽度,其默认值是 `max_width`的 `18%`。
- struct_variant_width, 结构体变量最大宽度,其默认值是 `max_width`的 `35%`。
- array_width, 数组最大宽度,其默认值是 `max_width`的 `60%`。
- chain_width, 链式结构最大宽度,其默认值是 `max_width`的 `60%`。
- single_line_if_else_max_width,单行 `if-else` 最大宽度,其默认值是 `max_width`的 `50%`。
这么多宽度设置管理起来比较麻烦,所以使用 `use_small_heuristics` 来管理更好。
**【反例】**
当`use_small_heuristics` 配置为 `Off` :
```rust
enum Lorem {
Ipsum,
Dolor(bool),
Sit {
amet: Consectetur,
adipiscing: Elit,
},
}
fn main() {
lorem("lorem", "ipsum", "dolor", "sit", "amet", "consectetur", "adipiscing");
let lorem = Lorem {
ipsum: dolor,
sit: amet,
};
let lorem = if ipsum {
dolor
} else {
sit
};
}
```
当`use_small_heuristics` 配置为 `Max` :
```rust
enum Lorem {
Ipsum,
Dolor(bool),
Sit { amet: Consectetur, adipiscing: Elit },
}
fn main() {
lorem("lorem", "ipsum", "dolor", "sit", "amet", "consectetur", "adipiscing");
let lorem = Lorem { ipsum: dolor, sit: amet };
let lorem = if ipsum { dolor } else { sit };
}
```
**【正例】**
`use_small_heuristics` 默认配置示例。
```rust
enum Lorem {
Ipsum,
Dolor(bool),
Sit { amet: Consectetur, adipiscing: Elit },
}
fn main() {
lorem(
"lorem",
"ipsum",
"dolor",
"sit",
"amet",
"consectetur",
"adipiscing",
);
let lorem = Lorem {
ipsum: dolor,
sit: amet,
};
let lorem = Lorem { ipsum: dolor };
let lorem = if ipsum { dolor } else { sit };
}
```
**【rustfmt 配置】**
此规则 Clippy 不可检测,由 rustfmt 自动格式化。
rustfmt 配置:
| 对应选项 | 可选值 | 是否 stable | 说明 |
| ------ | ---- | ---- | ---- |
| [`max_width`](https://rust-lang.github.io/rustfmt/?#max_width) | 100 | yes(默认)| 行最大宽度默认值|
|[`error_on_line_overflow`](https://rust-lang.github.io/rustfmt/?#error_on_line_overflow)| false(默认)| No (tracking issue: #3391)| 如果超过最大行宽设置则报错|
|[`use_small_heuristics`](https://rust-lang.github.io/rustfmt/?#use_small_heuristics)| Default(默认)Max(推荐) | Yes| 统一管理宽度设置|
## 单行规则
**【级别】** 建议
**【描述】**
当语言项内容为空时,即空函数,空结构体,空实现等,要保持单独一行。但是,当函数中只有一个表达式时,请不要保持单行。
**【反例】**
```rust
fn lorem() {
}
impl Lorem {
}
fn lorem() -> usize { 42 }
fn main() {
let lorem = Lorem {
foo: bar,
baz: ofo,
};
}
```
**【正例】**
```rust
fn lorem() {}
impl Lorem {}
fn lorem() -> usize {
42
}
fn main() {
let lorem = Lorem { foo: bar, baz: ofo };
}
```
**【rustfmt 配置】**
此规则 Clippy 不可检测,由 rustfmt 自动格式化。
rustfmt 配置:
| 对应选项 | 默认值 | 是否 stable | 说明 |
| ------ | ---- | ---- | ---- |
| [`empty_item_single_line`](https://rust-lang.github.io/rustfmt/?#empty_item_single_line) | true(默认) | No| 当语言项内容为空时,要保持单行 |
| [`fn_single_line`](https://rust-lang.github.io/rustfmt/?#fn_single_line) | false(默认) | No| 当函数中只有一个表达式时,不要保持单行 |
| [`struct_lit_single_line`](https://rust-lang.github.io/rustfmt/?#struct_lit_single_line) | true(默认) | No| 当结构体字面量中只有少量表达式时,要保持单行 |
## 换行样式以文件自动检测为主
**【级别】** 建议
**【描述】**
换行样式是基于每个文件自动检测的。 具有混合行尾的文件将转换为第一个检测到的行尾样式。
不同平台换行符不同:
- `Windows` 以 `\r\n`结尾。
- `Unix` 以 `\n` 结尾。
**【rustfmt 配置】**
此规则 Clippy 不可检测,由 rustfmt 自动格式化。
rustfmt 配置:
| 对应选项 | 可选值 | 是否 stable | 说明 |
| ------ | ---- | ---- | ---- |
| [`newline_style`](https://rust-lang.github.io/rustfmt/?#newline_style) | Auto(默认) | Yes| 换行样式以文件自动检测为主 |
## 结尾逗号规则
**【级别】** 建议
**【描述】**
1. 当多个字段在不同行时,在最后一个字段结尾添加逗号,如果在同一行,则不加逗号。
2. 在match分支中,如果包含了块,则不需要加逗号,否则需要加。
**【反例】**
```rust
// 当 `trailing_comma="Always"`
fn main() {
let Lorem { ipsum, dolor, sit, } = amet;
let Lorem {
ipsum,
dolor,
sit,
amet,
consectetur,
adipiscing,
} = elit;
}
// 当 `trailing_comma="Never"`
fn main() {
let Lorem { ipsum, dolor, sit } = amet;
let Lorem {
ipsum,
dolor,
sit,
amet,
consectetur,
adipiscing
} = elit;
}
// 当 `match_block_trailing_comma=true`
fn main() {
match lorem {
Lorem::Ipsum => {
println!("ipsum");
},
Lorem::Dolor => println!("dolor"),
}
}
```
**【正例】**
```rust
// 当 `trailing_comma="Vertical"`
fn main() {
let Lorem { ipsum, dolor, sit } = amet;
let Lorem {
ipsum,
dolor,
sit,
amet,
consectetur,
adipiscing,
} = elit;
}
// 当 `match_block_trailing_comma=false`
fn main() {
match lorem {
Lorem::Ipsum => {
println!("ipsum");
}
Lorem::Dolor => println!("dolor"),
}
}
```
**【rustfmt 配置】**
此规则 Clippy 不可检测,由 rustfmt 自动格式化。
rustfmt 配置:
| 对应选项 | 可选值 | 是否 stable | 说明 |
| ------ | ---- | ---- | ---- |
| [`trailing_comma`](https://rust-lang.github.io/rustfmt/?#trailing_comma) | "Vertical"(默认) | No | 当多个字段在不同行时,在最后一个字段结尾添加逗号,如果在同一行,则不加逗号|
| [`match_block_trailing_comma`](https://rust-lang.github.io/rustfmt/?#match_block_trailing_comma) | false(默认) | No| 在match分支中,如果包含了块,则不需要加逗号,否则需要加 |
================================================
FILE: src/safe-guides/code_style/comments/G.CMT.01.md
================================================
## G.CMT.01 在公开的返回`Result`类型的函数文档中增加 Error 注释
**【级别】** 建议
**【描述】**
在公开(pub)的返回`Result`类型的函数文档中,建议增加 `# Error` 注释来解释什么场景下该函数会返回什么样的错误类型,方便用户处理错误。
说明: 该规则可以通过 cargo clippy 来检测,但默认不会警告。
**【反例】**
```rust
#![warn(clippy::missing_errors_doc)]
use std::io;
// 不符合: Clippy 会警告 "warning: docs for function returning `Result` missing `# Errors` section"
pub fn read(filename: String) -> io::Result<String> {
unimplemented!();
}
```
**【正例】**
```rust
#![warn(clippy::missing_errors_doc)]
use std::io;
// 符合:增加了规范的 Errors 文档注释
/// # Errors
///
/// Will return `Err` if `filename` does not exist or the user does not have
/// permission to read it.
pub fn read(filename: String) -> io::Result<String> {
unimplemented!();
}
```
**【Lint 检测】**
| lint name | Clippy 可检测 | Rustc 可检测 | Lint Group | 默认 level |
| -------------------------------------------------------------------------------------------------- | ------------- | ------------ | ---------- | ---------- |
| [missing_errors_doc](https://rust-lang.github.io/rust-clippy/master/index.html#missing_errors_doc) | yes | no | Style | allow |
================================================
FILE: src/safe-guides/code_style/comments/G.CMT.02.md
================================================
## G.CMT.02 如果公开的API在某些情况下会发生Panic,则相应文档中需增加 Panic 注释
**【级别】** 要求
**【描述】**
在公开(pub)函数文档中,建议增加 `# Panic` 注释来解释该函数在什么条件下会 Panic,便于使用者进行预处理。
说明: 该规则通过 cargo clippy 来检测。默认不会警告。
**【反例】**
```rust
#![warn(clippy::missing_panics_doc)]
// 不符合:没有添加 Panic 相关的文档注释,Clippy会报错 "warning: docs for function which may panic missing `# Panics` section"。
pub fn divide_by(x: i32, y: i32) -> i32 {
if y == 0 {
panic!("Cannot divide by 0")
} else {
x / y
}
}
```
**【正例】**
```rust
#![warn(clippy::missing_panics_doc)]
// 符合:增加了规范的 Panic 注释
/// # Panics
///
/// Will panic if y is 0
pub fn divide_by(x: i32, y: i32) -> i32 {
if y == 0 {
panic!("Cannot divide by 0")
} else {
x / y
}
}
```
**【Lint 检测】**
| lint name | Clippy 可检测 | Rustc 可检测 | Lint Group | 默认 level |
| -------------------------------------------------------------------------------------------------- | ------------- | ------------ | ---------- | ---------- |
| [missing_panics_doc](https://rust-lang.github.io/rust-clippy/master/index.html#missing_panics_doc) | yes | no | Style | allow |
默认为 `allow`,但是此规则需要设置`#![warn(clippy::missing_panics_doc)]`。
================================================
FILE: src/safe-guides/code_style/comments/G.CMT.03.md
================================================
## G.CMT.03 在文档注释中要使用空格代替 tab
**【级别】** 建议
**【描述】**
Rust 代码风格中提倡使用**四个空格**代替tab,在文档注释中也应该统一使用**四个空格**。
**【反例】**
下面文档注释中使用了 tab。
```rust
// 不符合:文档注释中使用了 tab 缩进
///
/// Struct to hold two strings:
/// - first one
/// - second one
pub struct DoubleString {
///
/// - First String:
/// - needs to be inside here
first_string: String,
///
/// - Second String:
/// - needs to be inside here
second_string: String,
}
```
**【正例】**
```rust
// 符合:文档注释中使用了四个空格缩进
///
/// Struct to hold two strings:
/// - first one
/// - second one
pub struct DoubleString {
///
/// - First String:
/// - needs to be inside here
first_string: String,
///
/// - Second String:
/// - needs to be inside here
second_string: String,
}
```
**【Lint 检测】**
| lint name | Clippy 可检测 | Rustc 可检测 | Lint Group | 默认 level |
| ------------------------------------------------------------------------------------------------------ | ------------- | ------------ | ---------- | ---------- |
| [tabs_in_doc_comments](https://rust-lang.github.io/rust-clippy/master/index.html#tabs_in_doc_comments) | yes | no | Style | warn |
================================================
FILE: src/safe-guides/code_style/comments/P.CMT.01.md
================================================
## P.CMT.01 代码能做到自注释,文档要干练简洁
**【描述】**
一、代码能够做到自注释,避免冗余的普通代码注释。
注释固然很重要, 但最好的代码应当本身就是文档。有意义的类型名、函数名和变量名, 要远胜过要用注释解释的含糊不清的名字。当有意义的类型名、函数名和变量名还不能表达完整的语义时,再使用注释。
不要描述显而易见的现象, 永远不要用自然语言翻译代码作为注释。
二、文档注释要干练简洁:
1. 文档注释中内容用语应该尽量简短精干,不宜篇幅过长。请确保你的代码注释良好并且易于他人理解,好的注释能够传达上下文关系和代码目的。
2. 注释内容始终围绕两个关键点来构建:
- What : 用于阐述代码为了什么而实现。
- How : 用于阐述代码如何去使用。
3. 注释和文档注释使用的自然语言要保持一致。
4. Rust 项目文档应该始终基于 `rustdoc` 工具来构建,`rustdoc` 支持 Markdown 格式,为了使得文档更加美观易读,文档注释应该使用 Markdown 格式。
**【正例】**
模块级文档,来自于 Rust 标准库`std::vec`:
```rust
// 符合
//! # The Rust core allocation and collections library
//!
//! This library provides smart pointers and collections for managing
//! heap-allocated values.
//!
//! This library, like libcore, normally doesn’t need to be used directly
//! since its contents are re-exported in the [`std` crate](../std/index.html).
//! Crates that use the `#![no_std]` attribute however will typically
//! not depend on `std`, so they’d use this crate instead.
//!
//! ## Boxed values
//!
//! The [`Box`] type is a smart pointer type. There can only be one owner of a
//! [`Box`], and the owner can decide to mutate the contents, which live on the
//! heap.
```
普通文档注释示例,来自标准库`Vec::new`方法:
```rust
// 符合
/// Constructs a new, empty `Vec<T>`.
///
/// The vector will not allocate until elements are pushed onto it.
///
/// # Examples
///
/// ```
/// # #![allow(unused_mut)]
/// let mut vec: Vec<i32> = Vec::new();
/// ```
#[inline]
#[rustc_const_stable(feature = "const_vec_new", since = "1.39.0")]
#[stable(feature = "rust1", since = "1.0.0")]
pub const fn new() -> Self {
Vec { buf: RawVec::NEW, len: 0 }
}
```
================================================
FILE: src/safe-guides/code_style/comments/P.CMT.02.md
================================================
## P.CMT.02 注释应该有宽度限制
**【描述】**
每行注释的宽度不能过长,需要设置一定的宽度,不超过120,有助于提升可读性。
`rustfmt`中通过`comment_width`配合 `wrap_comments` 配置项,可将超过宽度限制的注释自动分割为多行。
注意:`rustfmt`的 `use_small_heuristics`配置项并不包括`comment_width`。
**【反例】**
```rust
// 不符合
// Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
```
**【正例】**
当 `comment_width=80` 且 `wrap_comments=true`时。
注意:这里 `wrap_comments`并未使用默认值,需要配置为 true。
```rust
// 符合
// Lorem ipsum dolor sit amet, consectetur adipiscing elit,
// sed do eiusmod tempor incididunt ut labore et dolore
// magna aliqua. Ut enim ad minim veniam, quis nostrud
// exercitation ullamco laboris nisi ut aliquip ex ea
// commodo consequat.
```
**【rustfmt 配置】**
| 对应选项 | 可选值 | 是否 stable | 说明 |
| ------ | ---- | ---- | ---- |
| [`comment_width`](https://rust-lang.github.io/rustfmt/?#comment_width) | 80(默认) | No| 指定一行注释允许的最大宽度 |
| [`wrap_comments`](https://rust-lang.github.io/rustfmt/?#wrap_comments) | false(默认),true(建议) | No| 运行多行注释按最大宽度自动换成多行注释 |
================================================
FILE: src/safe-guides/code_style/comments/P.CMT.03.md
================================================
## P.CMT.03 使用行注释而避免使用块注释
**【描述】**
尽量使用行注释(`//` 或 `///`),而非块注释。这是Rust社区的约定俗成。
对于文档注释,仅在编写模块级文档时使用 `//!`,在其他情况使用 `///`更好。
说明: `#![doc]` 和 `#[doc]` 对于简化文档注释有特殊作用,没有必要通过 `rustfmt` 将其强制转化为 `//!` 或 `///` 。
**【反例】**
```rust
// 不符合
/*
* Wait for the main task to return, and set the process error code
* appropriately.
*/
mod tests {
//! This module contains tests
// ...
}
```
**【正例】**
当 `normalize_comments = true` 时:
```rust
// 符合
// Wait for the main task to return, and set the process error code
// appropriately.
// 符合
// 在使用 `mod` 关键字定义模块时,在 `mod`之上使用 `///` 更好。
/// This module contains tests
mod tests {
// ...
}
// 符合
#[doc = "Example item documentation"]
pub enum Foo {}
```
**【rustfmt 配置】**
| 对应选项 | 可选值 | 是否 stable | 说明 |
| ------ | ---- | ---- | ---- |
| [`normalize_comments`](https://rust-lang.github.io/rustfmt/?#normalize_comments) | false(默认) true(推荐) | No| 将 `/**/` 注释转为 `//`|
| [`normalize_doc_attributes`](https://rust-lang.github.io/rustfmt/?#normalize_doc_attributes) | false(默认) | No| 将 `#![doc]` 和 `#[doc]` 注释转为 `//!` 和 `///` |
================================================
FILE: src/safe-guides/code_style/comments/P.CMT.04.md
================================================
## P.CMT.04 文件头注释包含版权说明
**【描述】**
文件头(即,模块级)注释应先包含版权说明。如果文件头注释需要增加其他内容,可以在版权说明下面补充。
可以包括:
1. 文件功能说明。
2. 作者。
3. 创建日期 和 最后修改日期。
4. 注意事项。
5. 开源许可证(比如, Apache 2.0, BSD, LGPL, GPL)。
6. 其他。
版权说明格式如下:
- 中文版:`版权所有(c)XXX 技术有限公司 2015-2022`。
- 英文版: `Copyright (c) XXX Technologies Co.Ltd. 2015-2022. All rights reserved. Licensed under Apache-2.0.`
其内容可以进行调整,参见下面详细说明:
- `2015-2022` 根据实际需要可以修改。2015是文件首次创建年份,2022是文件最后修改年份。可以只写一个创建年份,后续如果经常修改则无需修改版权声明。
- 如果是内部使用,则无需增加 `All rights reserved`。
- `Licensed under Apache-2.0.`,如果是开源则可以增加许可证声明。
编写版权注释时注意事项:
- 版权注释应该从文件头顶部开始写。
- 文件头注释首先包含“版权说明”,然后紧跟其他内容。
- 可选内容应按需添加,避免空有格式没有内容的情况。
- 保持统一格式,具体格式由项目或更大的范围统一制定。
- 保持版面工整,换行注意对齐。
**【正例】**
```rust
// 符合
// 版权所有(c)XXX 技术有限公司 2015-2022。
// Or
// 符合
// Copyright (c) XXX Technologies Co.Ltd. 2015-2022.
// All rights reserved. Licensed under Apache-2.0.
```
================================================
FILE: src/safe-guides/code_style/comments/P.CMT.05.md
================================================
## P.CMT.05 在注释中使用 `FIXME` 和 `TODO` 来帮助任务协作
**【描述】**
通过在注释中开启 `FIXME` 和 `TODO` 可以方便协作。正式发布版本可以不做此类标注。
注意:此条目不适于使用 `rustfmt`相关配置项 `report_fixme` 和 `report_todo`,在 `rustfmt` v2.0 中已经移除这两项配置。
**【正例】**
```rust
// 符合
// TODO(calebcartwright): consider enabling box_patterns feature gate
fn annotation_type_for_level(level: Level) -> AnnotationType {
match level {
Level::Bug | Level::Fatal | Level::Error => AnnotationType::Error,
Level::Warning => AnnotationType::Warning,
Level::Note => AnnotationType::Note,
Level::Help => AnnotationType::Help,
// FIXME(#59346): Not sure how to map these two levels
Level::Cancelled | Level::FailureNote => AnnotationType::Error,
Level::Allow => panic!("Should not call with Allow"),
}
}
```
================================================
FILE: src/safe-guides/code_style/comments.md
================================================
# 2.3 注释与文档
在 Rust 中,注释分为两类:普通注释和文档注释。普通注释使用 `//` 或 `/* ... */`,文档注释使用 `///`、`//!` 或 `/** ... **/`。
在原则和规则中提到「注释」时,包括普通注释和文档注释。当提到「文档」时,特指文档注释。
## 参考
1. [RFC 505: API 注释约定](https://github.com/rust-lang/rfcs/blob/master/text/0505-api-comment-conventions.md)
2. [RFC 1574: API 文档约定](https://github.com/rust-lang/rfcs/blob/master/text/1574-more-api-documentation-conventions.md)
3. [Making Great Docs with Rustdoc](https://www.tangram
gitextract_1j8v9haz/
├── .github/
│ └── workflows/
│ └── main.yml
├── .gitignore
├── Changelog.md
├── LICENSE
├── MakeFile
├── README.md
├── book.toml
├── mermaid-init.js
└── src/
├── SUMMARY.md
├── contribution.md
├── overview.md
└── safe-guides/
├── Appendix/
│ ├── best-practice/
│ │ ├── intro.md
│ │ ├── qa.md
│ │ └── tips.md
│ ├── cheat-sheet/
│ │ ├── Numbers/
│ │ │ └── float.md
│ │ └── README.md
│ ├── contribution.md
│ ├── dev_env.md
│ ├── old_guidelines.md
│ ├── optimizing/
│ │ └── intro.md
│ ├── rustc-flag.md
│ ├── templates/
│ │ ├── clippy.toml.md
│ │ ├── deny.toml.md
│ │ ├── intro.md
│ │ └── rustfmt.toml.md
│ ├── terms.md
│ ├── test/
│ │ ├── benchmark.md
│ │ ├── fuzz.md
│ │ └── unit_test.md
│ ├── test.md
│ ├── toc.md
│ └── tools/
│ ├── cargo-udeps.md
│ ├── intro.md
│ ├── noisy-clippy.md
│ └── rustfmt.md
├── code_style/
│ ├── comments/
│ │ ├── G.CMT.01.md
│ │ ├── G.CMT.02.md
│ │ ├── G.CMT.03.md
│ │ ├── P.CMT.01.md
│ │ ├── P.CMT.02.md
│ │ ├── P.CMT.03.md
│ │ ├── P.CMT.04.md
│ │ └── P.CMT.05.md
│ ├── comments.md
│ ├── fmt/
│ │ ├── P.FMT.01.md
│ │ ├── P.FMT.02.md
│ │ ├── P.FMT.03.md
│ │ ├── P.FMT.04.md
│ │ ├── P.FMT.05.md
│ │ ├── P.FMT.06.md
│ │ ├── P.FMT.07.md
│ │ ├── P.FMT.08.md
│ │ ├── P.FMT.09.md
│ │ ├── P.FMT.10.md
│ │ ├── P.FMT.11.md
│ │ ├── P.FMT.12.md
│ │ ├── P.FMT.13.md
│ │ ├── P.FMT.14.md
│ │ ├── P.FMT.15.md
│ │ └── P.FMT.16.md
│ ├── fmt.md
│ ├── naming/
│ │ ├── G.NAM.01.md
│ │ ├── G.NAM.02.md
│ │ ├── P.NAM.01.md
│ │ ├── P.NAM.02.md
│ │ ├── P.NAM.03.md
│ │ ├── P.NAM.04.md
│ │ ├── P.NAM.05.md
│ │ ├── P.NAM.06.md
│ │ ├── P.NAM.07.md
│ │ ├── P.NAM.08.md
│ │ └── P.NAM.09.md
│ └── naming.md
├── code_style.md
├── coding_practice/
│ ├── async-await/
│ │ ├── G.ASY.01.md
│ │ ├── G.ASY.02.md
│ │ ├── G.ASY.03.md
│ │ ├── G.ASY.04.md
│ │ ├── G.ASY.05.md
│ │ └── P.ASY.01.md
│ ├── async-await.md
│ ├── cargo/
│ │ ├── G.CAR.01.md
│ │ ├── G.CAR.02.md
│ │ ├── G.CAR.03.md
│ │ ├── G.CAR.04.md
│ │ ├── P.CAR.01.md
│ │ ├── P.CAR.02.md
│ │ ├── P.CAR.03.md
│ │ └── P.CAR.04.md
│ ├── cargo.md
│ ├── code-generation/
│ │ ├── P.CGN.01.md
│ │ └── P.CGN.02.md
│ ├── code-generation.md
│ ├── collections/
│ │ ├── G.CLT.01.md
│ │ └── P.CLT.01.md
│ ├── collections.md
│ ├── consts/
│ │ ├── G.CNS.01.md
│ │ ├── G.CNS.02.md
│ │ ├── G.CNS.03.md
│ │ ├── G.CNS.04.md
│ │ └── G.CNS.05.md
│ ├── consts.md
│ ├── control-flow/
│ │ ├── G.CTF.01.md
│ │ ├── G.CTF.02.md
│ │ ├── G.CTF.03.md
│ │ ├── G.CTF.04.md
│ │ ├── P.CTF.01.md
│ │ └── P.CTF.02.md
│ ├── control-flow.md
│ ├── data-type/
│ │ ├── G.TYP.01.md
│ │ ├── G.TYP.02.md
│ │ ├── G.TYP.03.md
│ │ ├── P.TYP.01.md
│ │ ├── array/
│ │ │ ├── G.TYP.ARR.01.md
│ │ │ ├── G.TYP.ARR.02.md
│ │ │ └── G.TYP.ARR.03.md
│ │ ├── array.md
│ │ ├── bool/
│ │ │ ├── G.TYP.BOL.01.md
│ │ │ ├── G.TYP.BOL.02.md
│ │ │ ├── G.TYP.BOL.03.md
│ │ │ ├── G.TYP.BOL.04.md
│ │ │ ├── G.TYP.BOL.05.md
│ │ │ ├── G.TYP.BOL.06.md
│ │ │ └── G.TYP.BOL.07.md
│ │ ├── bool.md
│ │ ├── char/
│ │ │ ├── G.TYP.CHR.01.md
│ │ │ ├── G.TYP.CHR.02.md
│ │ │ └── G.TYP.CHR.03.md
│ │ ├── char.md
│ │ ├── enum/
│ │ │ ├── G.TYP.ENM.01.md
│ │ │ ├── G.TYP.ENM.02.md
│ │ │ ├── G.TYP.ENM.03.md
│ │ │ ├── G.TYP.ENM.04.md
│ │ │ ├── G.TYP.ENM.05.md
│ │ │ ├── G.TYP.ENM.06.md
│ │ │ └── G.TYP.ENM.07.md
│ │ ├── enum.md
│ │ ├── float/
│ │ │ ├── G.TYP.FLT.01.md
│ │ │ ├── G.TYP.FLT.02.md
│ │ │ ├── G.TYP.FLT.03.md
│ │ │ ├── G.TYP.FLT.04.md
│ │ │ └── G.TYP.FLT.05.md
│ │ ├── float.md
│ │ ├── int/
│ │ │ ├── G.TYP.INT.01.md
│ │ │ ├── G.TYP.INT.02.md
│ │ │ └── G.TYP.INT.03.md
│ │ ├── int.md
│ │ ├── ref/
│ │ │ └── .keep
│ │ ├── ref.md
│ │ ├── slice/
│ │ │ ├── P.TYP.SLC.01.md
│ │ │ └── P.TYP.SLC.02.md
│ │ ├── slice.md
│ │ ├── struct/
│ │ │ ├── G.TYP.SCT.01.md
│ │ │ ├── G.TYP.SCT.02.md
│ │ │ ├── G.TYP.SCT.03.md
│ │ │ ├── P.TYP.SCT.01.md
│ │ │ └── P.TYP.SCT.02.md
│ │ ├── struct.md
│ │ ├── tuple/
│ │ │ └── G.TYP.TUP.01.md
│ │ ├── tuple.md
│ │ ├── unit/
│ │ │ └── .keep
│ │ ├── unit.md
│ │ ├── vec/
│ │ │ ├── G.TYP.VEC.01.md
│ │ │ ├── P.TYP.VEC.01.md
│ │ │ └── P.TYP.VEC.02.md
│ │ └── vec.md
│ ├── data-type.md
│ ├── error-handle/
│ │ ├── G.ERR.01.md
│ │ ├── G.ERR.02.md
│ │ ├── P.ERR.01.md
│ │ └── P.ERR.02.md
│ ├── error-handle.md
│ ├── expr/
│ │ ├── G.EXP.01.md
│ │ ├── G.EXP.02.md
│ │ ├── G.EXP.03.md
│ │ ├── G.EXP.04.md
│ │ ├── G.EXP.05.md
│ │ └── G.EXP.06.md
│ ├── expr.md
│ ├── fn-design/
│ │ ├── G.FUD.01.md
│ │ ├── G.FUD.02.md
│ │ ├── G.FUD.03.md
│ │ ├── G.FUD.04.md
│ │ ├── G.FUD.05.md
│ │ ├── G.FUD.06.md
│ │ ├── P.FUD.01.md
│ │ └── P.FUD.02.md
│ ├── fn-design.md
│ ├── generic/
│ │ ├── G.GEN.01.md
│ │ ├── G.GEN.02.md
│ │ ├── P.GEN.01.md
│ │ ├── P.GEN.02.md
│ │ ├── P.GEN.03.md
│ │ ├── P.GEN.04.md
│ │ └── P.GEN.05.md
│ ├── generic.md
│ ├── io/
│ │ ├── G.FIO.01.md
│ │ └── P.FIO.01.md
│ ├── io.md
│ ├── macros/
│ │ ├── G.MAC.01.md
│ │ ├── G.MAC.02.md
│ │ ├── P.MAC.01.md
│ │ ├── P.MAC.02.md
│ │ ├── decl/
│ │ │ ├── P.MAC.DCL.01.md
│ │ │ ├── P.MAC.DCL.02.md
│ │ │ ├── P.MAC.DCL.03.md
│ │ │ ├── P.MAC.DCL.04.md
│ │ │ ├── P.MAC.DCL.05.md
│ │ │ ├── P.MAC.DCL.06.md
│ │ │ ├── P.MAC.DCL.07.md
│ │ │ └── P.MAC.DCL.08.md
│ │ ├── decl.md
│ │ ├── proc/
│ │ │ ├── P.MAC.PRO.01.md
│ │ │ ├── P.MAC.PRO.02.md
│ │ │ ├── P.MAC.PRO.03.md
│ │ │ └── P.MAC.PRO.04.md
│ │ └── proc.md
│ ├── macros.md
│ ├── memory/
│ │ ├── box/
│ │ │ ├── G.MEM.BOX.01.md
│ │ │ ├── G.MEM.BOX.02.md
│ │ │ └── G.MEM.BOX.03.md
│ │ ├── box.md
│ │ ├── drop/
│ │ │ └── G.MEM.DRP.01.md
│ │ ├── drop.md
│ │ ├── lifetime/
│ │ │ ├── P.MEM.LFT.01.md
│ │ │ └── P.MEM.LFT.02.md
│ │ ├── lifetime.md
│ │ ├── smart-ptr/
│ │ │ └── P.MEM.SPT.01.md
│ │ └── smart-ptr.md
│ ├── memory.md
│ ├── module/
│ │ ├── G.MOD.01.md
│ │ ├── G.MOD.02.md
│ │ ├── G.MOD.03.md
│ │ ├── G.MOD.04.md
│ │ ├── G.MOD.05.md
│ │ ├── P.MOD.01.md
│ │ └── P.MOD.02.md
│ ├── module.md
│ ├── no-std/
│ │ ├── P.EMB.01.md
│ │ └── P.EMB.02.md
│ ├── no-std.md
│ ├── others/
│ │ ├── G.OTH.01.md
│ │ └── G.OTH.02.md
│ ├── others.md
│ ├── security/
│ │ ├── G.SEC.01.md
│ │ └── P.SEC.01.md
│ ├── security.md
│ ├── statics/
│ │ └── G.STV.01.md
│ ├── statics.md
│ ├── strings/
│ │ ├── G.STR.01.md
│ │ ├── G.STR.02.md
│ │ ├── G.STR.03.md
│ │ ├── G.STR.04.md
│ │ ├── G.STR.05.md
│ │ ├── P.STR.01.md
│ │ ├── P.STR.02.md
│ │ ├── P.STR.03.md
│ │ ├── P.STR.04.md
│ │ └── P.STR.05.md
│ ├── strings.md
│ ├── threads/
│ │ ├── lock/
│ │ │ ├── G.MTH.LCK.01.md
│ │ │ ├── G.MTH.LCK.02.md
│ │ │ ├── G.MTH.LCK.03.md
│ │ │ ├── G.MTH.LCK.04.md
│ │ │ └── P.MTH.LCK.01.md
│ │ ├── lock-free/
│ │ │ ├── P.MTH.LKF.01.md
│ │ │ └── P.MTH.LKF.02.md
│ │ ├── lock-free.md
│ │ └── lock.md
│ ├── threads.md
│ ├── traits/
│ │ ├── P.TRA.01.md
│ │ ├── std-builtin/
│ │ │ ├── G.TRA.BLN.01.md
│ │ │ ├── G.TRA.BLN.02.md
│ │ │ ├── G.TRA.BLN.03.md
│ │ │ ├── G.TRA.BLN.04.md
│ │ │ ├── G.TRA.BLN.05.md
│ │ │ ├── G.TRA.BLN.06.md
│ │ │ ├── G.TRA.BLN.07.md
│ │ │ ├── G.TRA.BLN.08.md
│ │ │ ├── G.TRA.BLN.09.md
│ │ │ ├── G.TRA.BLN.10.md
│ │ │ └── P.TRA.BLN.01.md
│ │ ├── std-builtin.md
│ │ ├── trait-object/
│ │ │ ├── P.TRA.OBJ.01.md
│ │ │ └── P.TRA.OBJ.02.md
│ │ └── trait-object.md
│ ├── traits.md
│ ├── unsafe_rust/
│ │ ├── G.UNS.01.md
│ │ ├── P.UNS.01.md
│ │ ├── P.UNS.02.md
│ │ ├── P.UNS.03.md
│ │ ├── ffi/
│ │ │ ├── P.UNS.FFI.01.md
│ │ │ ├── P.UNS.FFI.02.md
│ │ │ ├── P.UNS.FFI.03.md
│ │ │ ├── P.UNS.FFI.04.md
│ │ │ ├── P.UNS.FFI.05.md
│ │ │ ├── P.UNS.FFI.06.md
│ │ │ ├── P.UNS.FFI.07.md
│ │ │ ├── P.UNS.FFI.08.md
│ │ │ ├── P.UNS.FFI.09.md
│ │ │ ├── P.UNS.FFI.10.md
│ │ │ ├── P.UNS.FFI.11.md
│ │ │ ├── P.UNS.FFI.12.md
│ │ │ ├── P.UNS.FFI.13.md
│ │ │ ├── P.UNS.FFI.14.md
│ │ │ ├── P.UNS.FFI.15.md
│ │ │ ├── P.UNS.FFI.16.md
│ │ │ ├── P.UNS.FFI.17.md
│ │ │ └── P.UNS.FFI.18.md
│ │ ├── ffi.md
│ │ ├── glossary.md
│ │ ├── io/
│ │ │ └── P.UNS.FIO.01.md
│ │ ├── io.md
│ │ ├── mem/
│ │ │ ├── G.UNS.MEM.01.md
│ │ │ ├── P.UNS.MEM.01.md
│ │ │ ├── P.UNS.MEM.02.md
│ │ │ ├── P.UNS.MEM.03.md
│ │ │ ├── P.UNS.MEM.04.md
│ │ │ └── P.UNS.MEM.05.md
│ │ ├── mem.md
│ │ ├── raw_ptr/
│ │ │ ├── G.UNS.PTR.01.md
│ │ │ ├── G.UNS.PTR.02.md
│ │ │ ├── G.UNS.PTR.03.md
│ │ │ ├── P.UNS.PTR.01.md
│ │ │ ├── P.UNS.PTR.02.md
│ │ │ └── P.UNS.PTR.03.md
│ │ ├── raw_ptr.md
│ │ ├── safe_abstract/
│ │ │ ├── G.UNS.SAS.01.md
│ │ │ ├── G.UNS.SAS.02.md
│ │ │ ├── P.UNS.SAS.01.md
│ │ │ ├── P.UNS.SAS.02.md
│ │ │ ├── P.UNS.SAS.03.md
│ │ │ ├── P.UNS.SAS.04.md
│ │ │ ├── P.UNS.SAS.05.md
│ │ │ ├── P.UNS.SAS.06.md
│ │ │ ├── P.UNS.SAS.07.md
│ │ │ ├── P.UNS.SAS.08.md
│ │ │ └── P.UNS.SAS.09.md
│ │ ├── safe_abstract.md
│ │ ├── union/
│ │ │ ├── P.UNS.UNI.01.md
│ │ │ └── P.UNS.UNI.02.md
│ │ └── union.md
│ ├── unsafe_rust.md
│ ├── variables/
│ │ ├── G.VAR.01.md
│ │ ├── G.VAR.02.md
│ │ ├── G.VAR.03.md
│ │ ├── G.VAR.04.md
│ │ ├── P.VAR.01.md
│ │ └── P.VAR.02.md
│ └── variables.md
├── coding_practice.md
└── overview/
├── convention.md
└── why.md
Condensed preview — 353 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (741K chars).
[
{
"path": ".github/workflows/main.yml",
"chars": 1022,
"preview": "name: CI\non:\n push:\n branches: [ main ]\n pull_request:\n branches: [ main ]\n\n# Add explicit permissions - this is"
},
{
"path": ".gitignore",
"chars": 14,
"preview": "book\n.DS_Store"
},
{
"path": "Changelog.md",
"chars": 365,
"preview": "## V 0.2 评审版本发布\n\n任何事情如果想做到恰到好处,都需要一个进化的过程。编码规范也不例外。\n\n1. 统一了规范的文本格式与措辞。\n2. 经过考量,将一些原则变为了规则,并增加了自定义lint的说明。\n3. 删除了一些不需要放到规"
},
{
"path": "LICENSE",
"chars": 1092,
"preview": "MIT License\n\nCopyright (c) 2021 Rust Coding Guidelines (Unofficial)\n\nPermission is hereby granted, free of charge, to an"
},
{
"path": "MakeFile",
"chars": 423,
"preview": ".PHONY: deploy\n\ninit:\n\tgit worktree add -f /tmp/rustguidebook gh-pages\n\tgit worktree remove -f /tmp/rustguidebook\n\tgit w"
},
{
"path": "README.md",
"chars": 1532,
"preview": "# Rust 编码规范 中文版\n\n## 状态\n\n- 《Rust 编码规范》目前为 V 1.0 beta 试行版,改进内容参考 [Changelog](./Changelog.md)\n\n## 介绍\n\n据了解,Rust 社区内有些公司和组织都各"
},
{
"path": "book.toml",
"chars": 475,
"preview": "[book]\nauthors = [\"blackanger\"]\nlanguage = \"zh\"\nmultilingual = false\nsrc = \"src\"\ntitle = \"Rust 编码规范 V 1.0 beta\"\n\n[build]"
},
{
"path": "mermaid-init.js",
"chars": 40,
"preview": "mermaid.initialize({startOnLoad:true});\n"
},
{
"path": "src/SUMMARY.md",
"chars": 32677,
"preview": "# Rust 编码规范\n\n- [概述](./overview.md)\n - [为什么需要 Rust 编码规范](./safe-guides/overview/why.md)\n - [编码规范基本约定](./safe-guides"
},
{
"path": "src/contribution.md",
"chars": 98,
"preview": "# 贡献者指南\n\n目前项目处于初期,先以 issues 提建议为主,暂不支持 Pull Request。\n\n等项目整体结构确定以后,再开始接受 Pull Request。\n\n\n### 贡献名单\n\n"
},
{
"path": "src/overview.md",
"chars": 1317,
"preview": "# 1. 概述\n\n## 状态\n\n- 《Rust 编码规范》目前为 V 1.0 beta 试行版,改进内容参考 [Changelog](./Changelog.md)\n\n## 详细\n\n- [1.1 为什么需要 Rust 编码规范](./ove"
},
{
"path": "src/safe-guides/Appendix/best-practice/intro.md",
"chars": 53,
"preview": "# I.最佳实践\n\n## 列表\n\n- [Q&A](./qa.md)\n- [Tips](./tips.md)"
},
{
"path": "src/safe-guides/Appendix/best-practice/qa.md",
"chars": 13,
"preview": "# 初学者常见问题Q&A\n"
},
{
"path": "src/safe-guides/Appendix/best-practice/tips.md",
"chars": 5041,
"preview": "# Rust 编程技巧\n\n\n### 设计模式之构建者模式\n\n当需要很多构造函数,或构造含有很多可选配置项时,宜使用构建者模式\n\n**【描述】**\n\nRust 中没有默认的构造函数,都是自定义构造函数。\n\n如果需要多个构造函数,或者构造时需要"
},
{
"path": "src/safe-guides/Appendix/cheat-sheet/Numbers/float.md",
"chars": 1895,
"preview": "# Float Cheat Sheet\n\n> From: [https://github.com/brazzy/floating-point-gui.de](https://github.com/brazzy/floating-point-"
},
{
"path": "src/safe-guides/Appendix/cheat-sheet/README.md",
"chars": 177,
"preview": "# F.Cheat Sheet\n\n这里用于梳理 Rust 相关的 Cheat Sheet。\n\n- [数字]()\n - [浮点数](./safe-guides/Appendix/cheat-sheet/Numbers/float.md)"
},
{
"path": "src/safe-guides/Appendix/contribution.md",
"chars": 58,
"preview": "# J.贡献说明\n\n欢迎直接提交 Issues 或 PR 直接参与评审和完善(包括精简、增补新的规则)编码规范。\n\n"
},
{
"path": "src/safe-guides/Appendix/dev_env.md",
"chars": 5075,
"preview": "# A.开发环境\n\n## 编辑器推荐\n\nVSCode + Rust Analyzer 扩展 \n\n其他辅助vscode 扩展:\n\n[flowistry](https://github.com/willcrichton/flowistry) ,"
},
{
"path": "src/safe-guides/Appendix/old_guidelines.md",
"chars": 20805,
"preview": "# K.淘汰的规则\n\n--- \n\n\n## 常量\n\n### G.CNS.03 不宜将量大的数据结构定义为常量\n\n**淘汰原因**\n\n虽然常量本质上是会内联,但Rust 支持类似于复制消除(Copy Elision)的优化(非强制),而且在不断"
},
{
"path": "src/safe-guides/Appendix/optimizing/intro.md",
"chars": 17641,
"preview": "# G.优化指南\n\n---\n\n## 内容介绍\n\n- Rust 性能优化总则\n- Rust 性能优化准备工作\n- Rust 性能剖析工具介绍\n- 日常 Rust 开发性能优化的技巧总结\n- Rust 编译大小和编译时间优化技巧\n\n本次分享将围"
},
{
"path": "src/safe-guides/Appendix/rustc-flag.md",
"chars": 14616,
"preview": "# H.Rust编译器编译参数说明\n\n## Rustc 说明\n\n通过以下命令可以打印编译器相关参数选项:\n\n```rust\n\n$ rustc -h \n// 或\n$ rustc --help\n// 在 Nightly Rust 下,比 Sta"
},
{
"path": "src/safe-guides/Appendix/templates/clippy.toml.md",
"chars": 5660,
"preview": "# Clippy 模板\n\n有些 Clippy 的 Lint,依赖于一些配置项,如果不想要默认值,可以在 `clippy.toml` 中进行设置。\n\n```toml\n# for `disallowed_method`:\n# https://r"
},
{
"path": "src/safe-guides/Appendix/templates/deny.toml.md",
"chars": 1305,
"preview": "# Cargo Deny 配置模板\n\n[cargo-deny](https://github.com/EmbarkStudios/cargo-deny) 是检查 Cargo 依赖的一个 Lint 工具。它检查的范围包括:\n\n- Licen"
},
{
"path": "src/safe-guides/Appendix/templates/intro.md",
"chars": 132,
"preview": "# D.模版\n\n这里记录一些 rustfmt 和 clippy 等相关工具等配置文件模版。\n\n- [rustfmt](./rustfmt.toml.md)\n- [clippy](./clippy.toml.md)\n- [deny](./de"
},
{
"path": "src/safe-guides/Appendix/templates/rustfmt.toml.md",
"chars": 1853,
"preview": "# Rustfmt 模板\n\n为了方便 Rust 开发者,这里提供一个 Rustfmt 的模板,以供参考。\n\n以下内容可以放到 `rustfmt.toml` 或 `.rustfmt.toml` 文件中。因为部分选项还未稳定,所以要使用 `ca"
},
{
"path": "src/safe-guides/Appendix/terms.md",
"chars": 41999,
"preview": "# C.术语解释\n\n\n## 语言元素术语表\n\n术语 | 中文翻译 | 备注\n------------------------------- |-------------"
},
{
"path": "src/safe-guides/Appendix/test/benchmark.md",
"chars": 15144,
"preview": "# 基准测试\n\n\n\n说明: 借用 MogoDB 工程师 Patrick 的文章来了解 Rust 里做基准测试基本姿势。\n\n> 原文: [https://patrickfreed.github.io/rust/2021/10/15/makin"
},
{
"path": "src/safe-guides/Appendix/test/fuzz.md",
"chars": 370,
"preview": "# 模糊测试\n\n[模糊测试(Fuzz testing)](https://en.wikipedia.org/wiki/Fuzz_testing)是一种软件测试技术,用于通过向软件提供伪随机数据作为输入来发现安全性和稳定性问题。\n\n关于模糊测"
},
{
"path": "src/safe-guides/Appendix/test/unit_test.md",
"chars": 623,
"preview": "# 单元测试\n\nRust 支持单元测试。\n\n## 测试代码组织\n\n对于内部函数,单元测试代码最好放到业务代码的同一个模块下。\n\n对于外部接口,单元测试最好放到独立的 `tests` 目录。\n\n## 文档测试\n\n对所有对外接口进行文档测试是一"
},
{
"path": "src/safe-guides/Appendix/test.md",
"chars": 7,
"preview": "# B.测试\n"
},
{
"path": "src/safe-guides/Appendix/toc.md",
"chars": 830,
"preview": "# 附录\n\n- [A.开发环境](./dev_env.md)\n- [B.测试](./test.md)\n - [单元测试](./test/unit_test.md)\n - [基准测试](./test/benchmark.md)\n "
},
{
"path": "src/safe-guides/Appendix/tools/cargo-udeps.md",
"chars": 358,
"preview": "# Cargo Udeps \n\n[cargo-udeps](https://github.com/est31/cargo-udeps) 检查 `Cargo.toml` 中未使用的依赖。\n\n`cargo udeps` 对标的是` rust"
},
{
"path": "src/safe-guides/Appendix/tools/intro.md",
"chars": 495,
"preview": "# E.工具链\n\n这里介绍一些检测工具,比如 Cargo fmt 和 Cargo Clippy.\n\n\n\n## 参考资料\n\n1. [https://doc.rust-lang.org/rustc/lints/groups.html](http"
},
{
"path": "src/safe-guides/Appendix/tools/noisy-clippy.md",
"chars": 1079,
"preview": "# 在 Rust 生态中被拒绝的一些默认开启的lint\n\n来源:[https://github.com/dtolnay/noisy-clippy](https://github.com/dtolnay/noisy-clippy)\n\n以下按字"
},
{
"path": "src/safe-guides/Appendix/tools/rustfmt.md",
"chars": 9318,
"preview": "# Rustfmt 配置相关说明\n\n## 在 Stable Rust 下使用未稳定配置项的方法\n\n1. CI Job 可以分为 `Stable` 和 `Nightly`。在 `Stable CI` 下进行编译,在`Nightly CI`下执"
},
{
"path": "src/safe-guides/code_style/comments/G.CMT.01.md",
"chars": 1264,
"preview": "## G.CMT.01 在公开的返回`Result`类型的函数文档中增加 Error 注释\r\n\r\n**【级别】** 建议\r\n\r\n**【描述】**\r\n\r\n在公开(pub)的返回`Result`类型的函数文档中,建议增加 `# Error` 注"
},
{
"path": "src/safe-guides/code_style/comments/G.CMT.02.md",
"chars": 1331,
"preview": "## G.CMT.02 如果公开的API在某些情况下会发生Panic,则相应文档中需增加 Panic 注释\r\n\r\n**【级别】** 要求\r\n\r\n**【描述】**\r\n\r\n在公开(pub)函数文档中,建议增加 `# Panic` 注释来解释该"
},
{
"path": "src/safe-guides/code_style/comments/G.CMT.03.md",
"chars": 1369,
"preview": "## G.CMT.03 在文档注释中要使用空格代替 tab\r\n\r\n**【级别】** 建议\r\n\r\n**【描述】**\r\n\r\nRust 代码风格中提倡使用**四个空格**代替tab,在文档注释中也应该统一使用**四个空格**。\r\n\r\n**【反例"
},
{
"path": "src/safe-guides/code_style/comments/P.CMT.01.md",
"chars": 1692,
"preview": "## P.CMT.01 代码能做到自注释,文档要干练简洁\r\n\r\n**【描述】**\r\n\r\n一、代码能够做到自注释,避免冗余的普通代码注释。\r\n\r\n注释固然很重要, 但最好的代码应当本身就是文档。有意义的类型名、函数名和变量名, 要远胜过要用注"
},
{
"path": "src/safe-guides/code_style/comments/P.CMT.02.md",
"chars": 1181,
"preview": "## P.CMT.02 注释应该有宽度限制\r\n\r\n**【描述】**\r\n\r\n每行注释的宽度不能过长,需要设置一定的宽度,不超过120,有助于提升可读性。\r\n\r\n`rustfmt`中通过`comment_width`配合 `wrap_comm"
},
{
"path": "src/safe-guides/code_style/comments/P.CMT.03.md",
"chars": 1134,
"preview": "## P.CMT.03 使用行注释而避免使用块注释\r\n\r\n**【描述】**\r\n\r\n尽量使用行注释(`//` 或 `///`),而非块注释。这是Rust社区的约定俗成。\r\n\r\n对于文档注释,仅在编写模块级文档时使用 `//!`,在其他情况使"
},
{
"path": "src/safe-guides/code_style/comments/P.CMT.04.md",
"chars": 894,
"preview": "## P.CMT.04 文件头注释包含版权说明\r\n\r\n**【描述】**\r\n\r\n文件头(即,模块级)注释应先包含版权说明。如果文件头注释需要增加其他内容,可以在版权说明下面补充。\r\n\r\n可以包括:\r\n\r\n1. 文件功能说明。\r\n2. 作者。"
},
{
"path": "src/safe-guides/code_style/comments/P.CMT.05.md",
"chars": 821,
"preview": "## P.CMT.05 在注释中使用 `FIXME` 和 `TODO` 来帮助任务协作\r\n\r\n**【描述】**\r\n\r\n通过在注释中开启 `FIXME` 和 `TODO` 可以方便协作。正式发布版本可以不做此类标注。\r\n\r\n注意:此条目不适"
},
{
"path": "src/safe-guides/code_style/comments.md",
"chars": 560,
"preview": "# 2.3 注释与文档\n\n在 Rust 中,注释分为两类:普通注释和文档注释。普通注释使用 `//` 或 `/* ... */`,文档注释使用 `///`、`//!` 或 `/** ... **/`。\n\n在原则和规则中提到「注释」时,包括普"
},
{
"path": "src/safe-guides/code_style/fmt/P.FMT.01.md",
"chars": 1766,
"preview": "## P.FMT.01 使用 rustfmt 进行自动格式化代码\r\n\r\n**【描述】**\r\n\r\n应该总是在项目中添加 `rustfmt.toml` 或 `.rustfmt.toml`文件。即使它是空文件,这是向潜在的合作者表明你希望代码是"
},
{
"path": "src/safe-guides/code_style/fmt/P.FMT.02.md",
"chars": 358,
"preview": "## P.FMT.02 缩进使用空格而非制表符\r\n\r\n**【描述】**\r\n\r\n缩进要使用四个空格,不要使用制表符(`\\t`)代替。可以通过 IDE 或编辑器把缩进设置为四个空格。\r\n\r\n**【rustfmt 配置】**\r\n\r\n| 对应选项"
},
{
"path": "src/safe-guides/code_style/fmt/P.FMT.03.md",
"chars": 805,
"preview": "## P.FMT.03 行间距最大宽度空一行\r\n\r\n**【描述】**\r\n\r\n代码行之间,最小间隔 `0` 行,最大间隔`1`行。\r\n\r\n**【反例】**\r\n\r\n```rust\r\nfn foo() {\r\n println!(\"a\");"
},
{
"path": "src/safe-guides/code_style/fmt/P.FMT.04.md",
"chars": 3488,
"preview": "## P.FMT.04 语言项(Item)定义时左花括号(brace)位置应该与语言项保持同一行\r\n\r\n**【描述】**\r\n\r\n为了保持代码结构的良好可读性,Rust 中定义各种语言项,包括控制结构(`if / match` 等)、函数、"
},
{
"path": "src/safe-guides/code_style/fmt/P.FMT.05.md",
"chars": 3800,
"preview": "## P.FMT.05 存在多个标识符时应该保持块状(Block)缩进\r\n\r\n**【描述】**\r\n\r\n当在表达式或语言项定义中出现多个标识符,则应该让其保持块状风格缩进。\r\n\r\n**【反例】**\r\n\r\n```rust\r\nfn main() "
},
{
"path": "src/safe-guides/code_style/fmt/P.FMT.06.md",
"chars": 1455,
"preview": "## P.FMT.06 当有多行表达式操作时,操作符应该置于行首\r\n\r\n**【描述】**\r\n\r\n当有多行表达式操作时,操作符应该置于行首,这样有利于代码的可读性和可维护性。\r\n\r\n**【反例】**\r\n\r\n操作符置于行尾:\r\n\r\n```ru"
},
{
"path": "src/safe-guides/code_style/fmt/P.FMT.07.md",
"chars": 1682,
"preview": "## P.FMT.07 枚举变体和结构体字段都应左对齐\r\n\r\n**【描述】**\r\n\r\n对于自定义了判别式的枚举体,和有字段的结构体而言,默认只需要左对齐就可以。\r\n\r\n这个宽度可以设置为任意值,但默认是`0`。\r\n\r\n> 说明:此宽度并不是"
},
{
"path": "src/safe-guides/code_style/fmt/P.FMT.08.md",
"chars": 2553,
"preview": "## P.FMT.08 函数参数超过五个或导入模块个数超过四个需换行\r\n\r\n**【描述】**\r\n\r\n1. 五个以内函数参数可以置于一行,超过五个则使用「块」状缩进。\r\n2. 导入模块每行超过四个,则换行。\r\n\r\n**【反例】**\r\n\r\n当"
},
{
"path": "src/safe-guides/code_style/fmt/P.FMT.09.md",
"chars": 1869,
"preview": "## P.FMT.09 不同的场景,使用不同的空格风格\r\n\r\n**【描述】**\r\n\r\n1. 在冒号之后添加空格,在冒号之前不要加空格。\r\n2. 在范围(range)操作符(`..`和`..=`)前后不要使用空格。\r\n3. 在`+`或`=`"
},
{
"path": "src/safe-guides/code_style/fmt/P.FMT.10.md",
"chars": 2199,
"preview": "## P.FMT.10 `match` 分支应该具有良好的可读性\r\n\r\n**【描述】**\r\n\r\n1. 当match分支右侧代码体太长无法和`=>`置于同一行需要使用块(block)来包裹。\r\n2. 在match分支左侧匹配表达式前不要增加"
},
{
"path": "src/safe-guides/code_style/fmt/P.FMT.11.md",
"chars": 1997,
"preview": "## P.FMT.11 导入模块分组应该具有良好的可读性\r\n\r\n**【描述】**\r\n\r\n1. 导入同一模块的类型,应该置于同一个块内(`imports_granularity=\"Crate\"`)。\r\n2. 模块导入应该按以下规则进行分组(`"
},
{
"path": "src/safe-guides/code_style/fmt/P.FMT.12.md",
"chars": 1756,
"preview": "## P.FMT.12 声明宏分支应该具有良好的可读性\r\n\r\n**【描述】**\r\n\r\n1. 在声明宏中,模式匹配分支(`=>` 左侧)应该使用紧凑格式(`format_macro_matchers=true`)。\r\n2. 而分支代码体(`="
},
{
"path": "src/safe-guides/code_style/fmt/P.FMT.13.md",
"chars": 1103,
"preview": "## P.FMT.13 具名结构体字段初始化时不要省略字段名\r\n\r\n**【描述】**\r\n\r\n因为本规则依赖于rustfmt,而rustfmt会根据相应配置项对代码进行自动更改,为了确保不会因为rustfmt配置项的更改而导致代码错误,请在"
},
{
"path": "src/safe-guides/code_style/fmt/P.FMT.14.md",
"chars": 605,
"preview": "## P.FMT.14 extern 外部函数需要显式指定 C-ABI\r\n\r\n**【描述】**\r\n\r\n当使用 `extern` 指定外部函数时,建议显式指定 `C-ABI`。\r\n\r\n虽然 `extern` 不指定的话默认就是 `C-ABI"
},
{
"path": "src/safe-guides/code_style/fmt/P.FMT.15.md",
"chars": 1070,
"preview": "## P.FMT.15 解构元组的时候允许使用`..`来指代剩余元素\r\n\r\n**【描述】**\r\n\r\n`rustfmt` 可以由 `condense_wildcard_suffixes` 配置项来格式化此规则,其默认选项是 false,表示"
},
{
"path": "src/safe-guides/code_style/fmt/P.FMT.16.md",
"chars": 960,
"preview": "## P.FMT.16 不要将派生宏中多个不相关的特质合并为同一行\r\n\r\n**【描述】**\r\n\r\n不要将派生宏(Derive)中多个特质(trait)合并为同一行,这样可以增加代码可读性,明确语义。\r\n\r\n`rustfmt` 配置项 `m"
},
{
"path": "src/safe-guides/code_style/fmt.md",
"chars": 602,
"preview": "# 2.2 格式\n\n制定统一的编码风格,是为了提升代码的可读性,让日常代码维护和团队之间审查代码更加方便。\n\nRust 有自动化格式化工具 rustfmt ,可以帮助开发者摆脱手工调整代码格式的工作,提升生产力。但是,rustfmt 遵循什"
},
{
"path": "src/safe-guides/code_style/naming/G.NAM.01.md",
"chars": 3934,
"preview": "\r\n## G.NAM.01 使用统一的命名风格\r\n\r\n**【级别】** 要求\r\n\r\n**【描述】**\r\n\r\nRust 倾向于在“类型”级的结构中使用大驼峰(`UpperCamelCase`) 命名风格,在 “变量、值(实例)、函数名”等结构"
},
{
"path": "src/safe-guides/code_style/naming/G.NAM.02.md",
"chars": 3876,
"preview": "## G.NAM.02 类型转换函数命名需要遵循所有权语义\r\n\r\n**【级别】** 建议\r\n\r\n**【描述】**\r\n\r\n进行特定类型转换的方法名应该包含以下前缀:\r\n\r\n| 名称前缀 | 内存代价 | 所有权 "
},
{
"path": "src/safe-guides/code_style/naming/P.NAM.01.md",
"chars": 1221,
"preview": "## P.NAM.01 同一个crate中标识符的命名规则应该使用统一的词序\r\n\r\n**【描述】**\r\n\r\n具体选择什么样的词序并不重要,但务必要保证同一个 crate 内词序的一致性。\r\n若提供与标准库中相似功能的东西时,也要与标准库名"
},
{
"path": "src/safe-guides/code_style/naming/P.NAM.02.md",
"chars": 816,
"preview": "## P.NAM.02 为 cargo feature 命名时不应含有无意义的占位词\r\n\r\n**【描述】**\r\n\r\n给 [Cargo feature](http://doc.crates.io/manifest.html#the-feat"
},
{
"path": "src/safe-guides/code_style/naming/P.NAM.03.md",
"chars": 672,
"preview": "## P.NAM.03 标识符命名应该符合阅读习惯\r\n\r\n**【描述】**\r\n\r\n标识符的命名要清晰、明了,有明确含义,容易理解。符合英文阅读习惯的命名将明显提高代码可读性。\r\n\r\n一些好的实践包括但不限于:\r\n\r\n- 使用正确的英文单词并"
},
{
"path": "src/safe-guides/code_style/naming/P.NAM.04.md",
"chars": 788,
"preview": "## P.NAM.04 作用域越大命名越精确,反之应简短\r\n\r\n**【描述】**\r\n\r\n1. 对于全局函数、全局变量、宏、类型名、枚举命名,应当精确描述并全局唯一。\r\n2. 对于函数局部变量,或者结构体、枚举中的成员变量,在其命名能够准确表"
},
{
"path": "src/safe-guides/code_style/naming/P.NAM.05.md",
"chars": 2823,
"preview": "## P.NAM.05 用于访问或获取数据的 `getter` 类方法通常不要使用 `get_` 前缀\r\n\r\n**【描述】**\r\n\r\n因为 Rust 所有权语义的存在,此例子中两个方法的参数分别是共享引用 `&self` 和 独占引用 `&"
},
{
"path": "src/safe-guides/code_style/naming/P.NAM.06.md",
"chars": 2467,
"preview": "## P.NAM.06 遵循 `iter/iter_mut/into_iter` 规范来生成迭代器\r\n\r\n**【描述】**\r\n\r\n此规则包含两条基本子规则:\r\n\r\n1. 对于容纳 `U` 类型的容器 (container) ,其迭代器方法应"
},
{
"path": "src/safe-guides/code_style/naming/P.NAM.07.md",
"chars": 755,
"preview": "## P.NAM.07 避免使用语言内置保留字、关键字、内置类型和`trait`等特殊名称\r\n\r\n**【描述】**\r\n\r\n命名必须要避免使用语言内置的保留字、关键字、内置类型和`trait`等特殊名称。 具体可以参考[The Rust Re"
},
{
"path": "src/safe-guides/code_style/naming/P.NAM.08.md",
"chars": 567,
"preview": "## P.NAM.08 避免在变量的命名中添加类型标识\r\n\r\n**【描述】**\r\n\r\n因为 Rust 语言类型系统崇尚显式的哲学,所以不需要在变量命名中也添加关于类型的标识。\r\n\r\n**【反例】**\r\n\r\n```rust\r\nlet acc"
},
{
"path": "src/safe-guides/code_style/naming/P.NAM.09.md",
"chars": 339,
"preview": "## P.NAM.09 定义全局静态变量时需加前缀`G_`以便和常量有所区分\r\n\r\n**【描述】**\r\n\r\n为了提升代码可读性和可维护性,有必要将常量的命名和全局静态变量加以区分。所以在定义全局静态变量时,需要以前缀`G_`命名。\r\n\r\n\r"
},
{
"path": "src/safe-guides/code_style/naming.md",
"chars": 93,
"preview": "# 2.1 命名\n\n好的命名风格能让我们快速地了解某个名字代表的含义(类型、变量、函数、常量、宏等),甚至能凸显其在整个代码上下文中的语义。命名管理对提升代码的可读性和维护性相当重要。\n"
},
{
"path": "src/safe-guides/code_style.md",
"chars": 68,
"preview": "# 2. 代码风格\n\n代码风格包含标识符的命名风格、排版与格式风格、注释风格等。一致的编码习惯与风格,可以提高代码可读性和可维护性。\n\n"
},
{
"path": "src/safe-guides/coding_practice/async-await/G.ASY.01.md",
"chars": 762,
"preview": "## G.ASY.01 在 `async` 块或函数中调用 `async` 函数或闭包请不要忘记添加`.await`\n\n**【级别】** 建议\n\n**【描述】**\n\n在此条件下 `.await` 语句通常为必须的。\n\n**【反例】**\n\n`"
},
{
"path": "src/safe-guides/coding_practice/async-await/G.ASY.02.md",
"chars": 2585,
"preview": "## G.ASY.02 在跨 `await` 调用中,需要对其持有的同步互斥锁进行处理\n\n**【级别】** 要求\n\n**【描述】**\n\n同步互斥锁本来就不是为异步上下文跨 `await` 调用而设计的,在这种场景中使用同步互斥锁容易造成死锁"
},
{
"path": "src/safe-guides/coding_practice/async-await/G.ASY.03.md",
"chars": 1639,
"preview": "## G.ASY.03 在跨 `await` 调用中,需要对其持有 `RefCell` 的引用进行处理\n\n**【级别】** 要求\n\n**【描述】**\n\n使用 `RefCell` 的独占(可变)借用会导致 Panic。因为 `RefCell`"
},
{
"path": "src/safe-guides/coding_practice/async-await/G.ASY.04.md",
"chars": 723,
"preview": "## G.ASY.04 避免定义不必要的异步函数\n\n**【级别】** 建议\n\n**【描述】**\n\n如果一个异步函数内部没有任何异步代码,相比一个同步函数,它会产生额外的调用成本。\n\n**【反例】**\n\n```rust\n// 不符合\n#[wa"
},
{
"path": "src/safe-guides/coding_practice/async-await/G.ASY.05.md",
"chars": 437,
"preview": "## G.ASY.05 避免在异步处理过程中包含阻塞操作\n\n**【级别】** 建议\n\n**【描述】**\n\n避免在异步编程中使用阻塞操作。\n\n**【反例】**\n\n不要在异步流程中使用阻塞操作函数\n\n```rust\nuse std::erro"
},
{
"path": "src/safe-guides/coding_practice/async-await/P.ASY.01.md",
"chars": 91,
"preview": "## P.ASY.01 异步编程并不适合所有场景,计算密集型场景应该考虑同步编程\n\n**【描述】**\n\n异步编程适合 I/O 密集型应用,如果是计算密集型场景应该考虑使用同步编程。\n"
},
{
"path": "src/safe-guides/coding_practice/async-await.md",
"chars": 257,
"preview": "# 3.18 异步编程\n\n`async / await` 是 Rust 语言用于编写像同步代码一样的异步函数的内置工具。`async` 将一个代码块转化为一个实现了名为 `Future` 的特质 (trait)\n的状态机。虽然在同步方法中调"
},
{
"path": "src/safe-guides/coding_practice/cargo/G.CAR.01.md",
"chars": 363,
"preview": "## G.CAR.01 当项目是可执行程序而非库时,建议使用 `src/main.rs` 和 `src/lib.rs` 模式\n\n**【级别】** 建议\n\n**【描述】**\n\n`crate` 结构类似于:\n\n```text\nsrc/\n --"
},
{
"path": "src/safe-guides/coding_practice/cargo/G.CAR.02.md",
"chars": 1441,
"preview": "## G.CAR.02 Crate 的 Cargo.toml 中应该包含必要的元信息\n\n**【级别】** 建议\n\n**【描述】**\n\n在 Cargo.toml 中应该包含必要的元信息,以便使用者知道它的作用。\n此外,若要将 `crate` "
},
{
"path": "src/safe-guides/coding_practice/cargo/G.CAR.03.md",
"chars": 1095,
"preview": "## G.CAR.03 Feature 命名应该避免否定式或多余的前后缀\n\n**【级别】** 建议\n\n**【描述】**\n\nFeature 命名应该避免出现 `no-` 或 `not-` 之类的否定前缀,或诸如 `use-`,`with-` "
},
{
"path": "src/safe-guides/coding_practice/cargo/G.CAR.04.md",
"chars": 847,
"preview": "## G.CAR.04 Cargo.toml 中依赖包版本不应使用通配符\n\n**【级别】** 要求\n\n**【描述】**\n\n依赖的包必须指定具体的语义版本。关于语义版本说明参见:[The Cargo Book: SemVer Compatib"
},
{
"path": "src/safe-guides/coding_practice/cargo/P.CAR.01.md",
"chars": 243,
"preview": "## P.CAR.01 应该尽量把项目划分为合理的 crate 组合\n\n**【描述】**\n\n将整个项目按一定逻辑划分为合理的 crate,在工程方面有利于组件化。并且 crate 是 Rust 的编译单元,也有助于提升编译速度。\n\n但需要注"
},
{
"path": "src/safe-guides/coding_practice/cargo/P.CAR.02.md",
"chars": 197,
"preview": "## P.CAR.02 不要滥用 Features\n\n**【描述】**\n\nRust 的 features 提供了方便的条件编译功能。从软件工程来说,features 适合应用于可选功能。\n\n在使用 features 的时候,应该考虑到底是不"
},
{
"path": "src/safe-guides/coding_practice/cargo/P.CAR.03.md",
"chars": 118,
"preview": "## P.CAR.03 使用 `cargo features` 来代替 `--cfg` 条件编译参数\n\n**【描述】**\n\n`cargo features` 为 Rust 原生的条件编译,可用于代替 `--cfg` 参数且兼容性更好。\n"
},
{
"path": "src/safe-guides/coding_practice/cargo/P.CAR.04.md",
"chars": 107,
"preview": "## P.CAR.04 宜使用 `cfg!` 来代替 `#[cfg]`\n\n**【描述】**\n\n`cfg!` 和正常代码一样,会检查全部函数逻辑,而 `#[cfg]` 是条件编译,则会跳过一些 Dead Code。\n"
},
{
"path": "src/safe-guides/coding_practice/cargo.md",
"chars": 69,
"preview": "# 3.15 包管理\n\nCargo 不仅仅是包管理,它还是一个 Workflow 工具。这一节包含 Cargo 和 Crate 相关内容。"
},
{
"path": "src/safe-guides/coding_practice/code-generation/P.CGN.01.md",
"chars": 1023,
"preview": "# P.CGN.01 代码生成要按情况选择使用过程宏还是 `build.rs`\n\n**【描述】**\n\n用过程宏生进行代码生成,比如生成新类型或函数,有一个缺点就是:IDE 无法识别它们,影响开发体验。\n\n但是使用 `build.rs` 生成"
},
{
"path": "src/safe-guides/coding_practice/code-generation/P.CGN.02.md",
"chars": 902,
"preview": "# P.CGN.02 `build.rs` 生成的代码要保证没有任何警告\n\n**【描述】**\n\n`build.rs` 生成的代码(codegen),要通过或忽略 clippy 检查,不要让库的使用者或应用用户自行忽略\n\ncodegen 库要"
},
{
"path": "src/safe-guides/coding_practice/code-generation.md",
"chars": 98,
"preview": "# 代码生成\n\nRust 中代码生成的方式包括宏 与 `build.rs` 两种方式。关于宏,有独立的规范章节,本章节规范内容包括:\n\n- `build.rs` 使用规范\n- 代码生成相关其他规范"
},
{
"path": "src/safe-guides/coding_practice/collections/G.CLT.01.md",
"chars": 789,
"preview": "## G.CLT.01 非必要情况下,不要使用`LinkedList`,而用`Vec`或`VecDeque`代替\n\n**【级别】** 建议\n\n**【描述】**\n\n一般情况下,有 `Vec`和`VecDeque` 性能更好。`LinkedLi"
},
{
"path": "src/safe-guides/coding_practice/collections/P.CLT.01.md",
"chars": 922,
"preview": "## P.CLT.01 创建HashMap、VecDeque时,可以预先分配大约足够的容量来避免后续操作中产生多次分配\n\n**【描述】**\n\n预分配足够的容量,避免后续内存分配,可以提升代码性能。\n\n**【反例】**\n\n```rust\nus"
},
{
"path": "src/safe-guides/coding_practice/collections.md",
"chars": 739,
"preview": "# 3.8 集合类型\n\nRust 中的集合类型包括四大类:\n\n- 线性序列: [`Vec`](https://doc.rust-lang.org/stable/std/vec/struct.Vec.html), [`VecDeque`]("
},
{
"path": "src/safe-guides/coding_practice/consts/G.CNS.01.md",
"chars": 755,
"preview": "## G.CNS.01 对于科学计算中涉及浮点数近似值的常量宜使用预定义常量\r\n\r\n**【级别】** 建议\r\n\r\n**【描述】**\r\n\r\nRust标准库中已经提供了一些特殊常量的定义,其精确度通常会比开发者自行定义的高,所以若考虑数值精确度"
},
{
"path": "src/safe-guides/coding_practice/consts/G.CNS.02.md",
"chars": 892,
"preview": "## G.CNS.02 不应断言常量布尔类型\r\n\r\n**【级别】** 建议\r\n\r\n**【描述】**\r\n\r\n此类语句会被编译器优化掉。最好直接使用 `panic!` 或 `unreachable!`代替。\r\n\r\n**【反例】**\r\n\r\n```"
},
{
"path": "src/safe-guides/coding_practice/consts/G.CNS.03.md",
"chars": 1346,
"preview": "## G.CNS.03 不应将内部可变性容器声明为常量\r\n\r\n**【级别】** 要求\r\n\r\n**【描述】**\r\n\r\n由于常量有内联的特性。若将一个内容可变容器声明为常量,那么在引用它的时候同样会新建一个实例,这样会破坏内容可变容器的使用目的"
},
{
"path": "src/safe-guides/coding_practice/consts/G.CNS.04.md",
"chars": 972,
"preview": "## G.CNS.04 不应在常量定义中增加显式的 `'static` 生命周期\r\n\r\n**【级别】** 要求\r\n\r\n**【描述】**\r\n\r\n在常量和静态变量声明时已经默认含有隐式的`'static`生命周期,所以不需要额外增加显式`'st"
},
{
"path": "src/safe-guides/coding_practice/consts/G.CNS.05.md",
"chars": 847,
"preview": "## G.CNS.05 对于适用 `const fn` 的函数或方法宜尽可能地使用 `const fn`\r\n\r\n**【级别】** 建议\r\n\r\n**【描述】**\r\n\r\n函数或方法缺失`const`关键词时无法被指派给常量。\r\n\r\n但是要注意"
},
{
"path": "src/safe-guides/coding_practice/consts.md",
"chars": 158,
"preview": "# 3.1 常量\n\n在 Rust 中,常量有两种用途:\n\n- 编译时常量(Compile-time constants)\n- 编译时求值 (CTEF, compile-time evaluable functions)\n\n常量命名风格指南请"
},
{
"path": "src/safe-guides/coding_practice/control-flow/G.CTF.01.md",
"chars": 1120,
"preview": "## G.CTF.01 当需要通过多个`if`比较大小来区分不同情况时,优先使用`match`和`cmp`来代替`if`表达式\n\n**【级别】** 建议\n\n**【描述】**\n\n在使用多个`if-else`来对不同情况进行区分时,使用 `ma"
},
{
"path": "src/safe-guides/coding_practice/control-flow/G.CTF.02.md",
"chars": 1063,
"preview": "## G.CTF.02 `if`条件表达式分支中如果包含了`else if`分支也应该包含`else`分支\n\n**【级别】** 建议\n\n**【描述】**\n\n这样做有助于代码逻辑更加健壮清晰,在一些要求严格的编码规范中要求这么做,比如《MIS"
},
{
"path": "src/safe-guides/coding_practice/control-flow/G.CTF.03.md",
"chars": 791,
"preview": "## G.CTF.03 如果要通过 `if` 条件表达式来判断是否 Panic,请优先使用断言\n\n**【级别】** 建议\n\n**【描述】**\n\n略\n\n**【反例】**\n\n```rust\nlet sad_people: Vec<&str> ="
},
{
"path": "src/safe-guides/coding_practice/control-flow/G.CTF.04.md",
"chars": 505,
"preview": "## G.CTF.04 在 Match 分支的 Guard 语句中不要使用带有副作用的条件表达式\n\n**【级别】** 建议\n\n**【描述】**\n\n因为在 mactch 分支中, 匹配几次就会执行 Guard 几次。如果携带副作用,会产生意料"
},
{
"path": "src/safe-guides/coding_practice/control-flow/P.CTF.01.md",
"chars": 2676,
"preview": "## P.CTF.01 避免滥用迭代器\n\n**【描述】**\n\n迭代器虽然是 Rust 中比较推崇的方式,但也没必要过度使用它。总之,如果使用迭代器让代码太复杂,就考虑换个非迭代器的方式实现吧。\n\n**【反例】**\n\n创建一个 Matrix变"
},
{
"path": "src/safe-guides/coding_practice/control-flow/P.CTF.02.md",
"chars": 397,
"preview": "## P.CTF.02 优先使用模式匹配而非判断后再取值\n\n**【描述】**\n\nRust 中 模式匹配 是惯用法,而不是通过 `if` 判断值是否相等。\n\n**【反例】**\n\n ```rust\n let opt: Option<_> = ."
},
{
"path": "src/safe-guides/coding_practice/control-flow.md",
"chars": 44,
"preview": "# 3.6 控制流程\n\nRust中流程控制也是属于表达式,但在本规范中将其独立出来。\n\n"
},
{
"path": "src/safe-guides/coding_practice/data-type/G.TYP.01.md",
"chars": 3503,
"preview": "## G.TYP.01 类型转换尽可能使用安全的转换函数代替 `as`\n\n**【级别】** 建议\n\n**【描述】**\n\n当在数字类型之间转换时,需要注意的是,如果要确保不会存在有损转换(lossy conversion),就不要使用 `as"
},
{
"path": "src/safe-guides/coding_practice/data-type/G.TYP.02.md",
"chars": 851,
"preview": "## G.TYP.02 数字字面量在使用的时候应该明确标注类型\n\n**【级别】** 建议\n\n**【描述】**\n\n如果数字字面量没有被指定具体类型,那么单靠类型推导,整数类型会被默认绑定为 `i32` 类型,而浮点数则默认绑定为 `f64`"
},
{
"path": "src/safe-guides/coding_practice/data-type/G.TYP.03.md",
"chars": 954,
"preview": "## G.TYP.03 不要用数字类型边界值判断能否安全转换,而应使用 `try_from` 方法\n\n**【级别】** 建议\n\n**【描述】**\n\n在 Rust 中 `From` 代表不能失败的转换,而 `TryFrom` 则允许返回错误"
},
{
"path": "src/safe-guides/coding_practice/data-type/P.TYP.01.md",
"chars": 339,
"preview": "## P.TYP.01 必要时,应使类型可以表达更明确的语义,而不是只是直接使用原生类型\n\n**【描述】**\n\n在类型中表达语义,可以增加代码的可读性。\n\n**【反例】**\n\n```rust\nfn main() {\n // 不符合\n"
},
{
"path": "src/safe-guides/coding_practice/data-type/array/G.TYP.ARR.01.md",
"chars": 1127,
"preview": "## G.TYP.ARR.01 创建大全局数组时宜使用静态变量而非常量\n\n**【级别】** 建议\n\n**【描述】**\n\n因为常量会内联,对于大的数组,通常情况下,会使用其引用,使用静态变量定义更好。\n\n栈上的数组大小以不超过 512KiB "
},
{
"path": "src/safe-guides/coding_practice/data-type/array/G.TYP.ARR.02.md",
"chars": 687,
"preview": "## G.TYP.ARR.02 使用数组索引时禁止越界访问\n\n**【级别】** 要求\n\n**【描述】**\n\n越界访问在运行时会 Panic!\n\n**【反例】**\n\n```rust\n// 不符合\nlet x = [1, 2, 3, 4];\n"
},
{
"path": "src/safe-guides/coding_practice/data-type/array/G.TYP.ARR.03.md",
"chars": 1236,
"preview": "## G.TYP.ARR.03 当数组元素为原生数据类型(Primitive),排序时优先选用非稳定排序\n\n**【级别】** 建议\n\n**【描述】**\n\n稳定排序会消耗更多的内存和 CPU 周期,相对而言,非稳定排序性能更佳。\n\n当然,在"
},
{
"path": "src/safe-guides/coding_practice/data-type/array.md",
"chars": 153,
"preview": "# 数组\n\n这里指固定长度数组。注意,不同长度的数组,被视为不同的类型。比如 `[T;1]`和 `[T;3]` 是两种不同的类型。\n\n从 Rust 1.51 版本开始,稳定了常量泛型(const generics)功能,形如 `[T;1]`"
},
{
"path": "src/safe-guides/coding_practice/data-type/bool/G.TYP.BOL.01.md",
"chars": 1590,
"preview": "## G.TYP.BOL.01 不应将布尔值和布尔字面量进行比较\n\n**【级别】** 要求\n\n**【描述】**\n\n在 Rust 中,返回为布尔值的表达式或函数值可以直接当作布尔值使用。\n\n总之,使用布尔表达式的时候,要尽可能地简洁明了。\n\n"
},
{
"path": "src/safe-guides/coding_practice/data-type/bool/G.TYP.BOL.02.md",
"chars": 1018,
"preview": "## G.TYP.BOL.02 如果 match 匹配表达式为布尔类型,宜使用 `if` 表达式来代替\n\n**【级别】** 建议\n\n**【描述】**\n\n对于布尔表达式更倾向于使用 `if ... else ...`,相比 `match` 模"
},
{
"path": "src/safe-guides/coding_practice/data-type/bool/G.TYP.BOL.03.md",
"chars": 665,
"preview": "## G.TYP.BOL.03 不应将数字类型转换为布尔值\n\n**【级别】** 要求\n\n**【描述】**\n\n这可能会让布尔值在内存中的表示无效。\n\n**【反例】**\n\n```rust\nlet x = 1_u8;\nunsafe {\n /"
},
{
"path": "src/safe-guides/coding_practice/data-type/bool/G.TYP.BOL.04.md",
"chars": 844,
"preview": "## G.TYP.BOL.04 禁止在if表达式条件中使用块结构\n\n**【级别】** 要求\n\n**【描述】**\n\n为了增加可读性。\n\n**【反例】**\n\n```rust\n// 不符合\nif { true } { /* ... */ }\n\n#"
},
{
"path": "src/safe-guides/coding_practice/data-type/bool/G.TYP.BOL.05.md",
"chars": 849,
"preview": "## G.TYP.BOL.05 非必要时,布尔运算应使用逻辑运算符( `&&/||`)而非位运算符 (`&/|`)\n\n**【级别】** 建议\n\n**【描述】**\n\n位运算不支持短路(short-circuiting),所以会影响性能。逻辑运"
},
{
"path": "src/safe-guides/coding_practice/data-type/bool/G.TYP.BOL.06.md",
"chars": 395,
"preview": "## G.TYP.BOL.06 不应使用数字代替布尔值\n\n**【级别】** 要求\n\n**【描述】**\n\nRust 中布尔值就是 `true` 和 `false`。 不要试图使用数字 `1` 和 `0` 来代替布尔值。\n\n虽然 布尔值 可"
},
{
"path": "src/safe-guides/coding_practice/data-type/bool/G.TYP.BOL.07.md",
"chars": 461,
"preview": "## G.TYP.BOL.07 使用 `.not()` 方法代替逻辑取反运算符 (`!`)\n\n**【级别】** 建议\n\n**【描述】**\n\n逻辑取反运算符 (`!`) 是前缀一元运算符,相对较长的逻辑表达式来说很不显眼。\n\n理解业务逻辑时,"
},
{
"path": "src/safe-guides/coding_practice/data-type/bool.md",
"chars": 38,
"preview": "# 布尔\n\nRust 中的布尔类型有 `true`和`false`两种值。\n"
},
{
"path": "src/safe-guides/coding_practice/data-type/char/G.TYP.CHR.01.md",
"chars": 597,
"preview": "## G.TYP.CHR.01 不应将字符字面量强制转换为 `u8`\n\n**【级别】** 建议\n\n**【描述】**\n\n应该使用字节字面量,而不应使用字符字面量强转为 `u8`。\n\n**【反例】**\n\n```rust\n// 不符合\n'x' "
},
{
"path": "src/safe-guides/coding_practice/data-type/char/G.TYP.CHR.02.md",
"chars": 674,
"preview": "## G.TYP.CHR.02 字符串方法中如果需要单个字符的值作为参数,宜使用字符而非字符串\n\n**【级别】** 建议\n\n**【描述】**\n\n大部分情况下,使用字符比用字符串性能更好。\n\n**【反例】**\n\n```rust\n// 不符合\n"
},
{
"path": "src/safe-guides/coding_practice/data-type/char/G.TYP.CHR.03.md",
"chars": 996,
"preview": "## G.TYP.CHR.03 需要将整数转换为字符时,应使用安全转换函数,而非 `transmute`\n\n**【级别】** 要求\n\n**【描述】**\n\n并非每个整数都对应一个合法的 Unicode 标量值,使用 `transmute` 转"
},
{
"path": "src/safe-guides/coding_practice/data-type/char.md",
"chars": 100,
"preview": "# 字符\n\n在 Rust 中,字符是一个合法的 Unicode 标量值(Unicode scalar value),一个字符大小为 4 字节,对应一个 Unicode 码位(CodePoint)。\n\n"
},
{
"path": "src/safe-guides/coding_practice/data-type/enum/G.TYP.ENM.01.md",
"chars": 1680,
"preview": "## G.TYP.ENM.01 合理使用`map`和`and_then`方法\n\n**【级别】** 建议\n\n**【描述】**\n\n在标准库中内置的一些 Enum 类型中提供了一些方便的组合算子,比如 `map` 和 `and_then`。\n\n-"
},
{
"path": "src/safe-guides/coding_practice/data-type/enum/G.TYP.ENM.02.md",
"chars": 1434,
"preview": "## G.TYP.ENM.02 不应自行创建空枚举\n\n**【级别】** 建议\n\n**【描述】**\n\n在 Rust 中 只有 `never` 类型(`!`)才是唯一合法表达 无法被实例化类型 的类型。但目前 `never` 类型还未稳定,只能"
},
{
"path": "src/safe-guides/coding_practice/data-type/enum/G.TYP.ENM.03.md",
"chars": 1275,
"preview": "## G.TYP.ENM.03 在使用类似 C 语言的枚举写法且使用`repr(isize/usize)` 布局时注意 32位架构上截断的问题\n\n**【级别】** 建议\n\n**【描述】**\n\n在使用类似 C 语言的枚举写法且使用`repr"
},
{
"path": "src/safe-guides/coding_practice/data-type/enum/G.TYP.ENM.04.md",
"chars": 1350,
"preview": "## G.TYP.ENM.04 不宜在`use`语句中引入Enum的全部变体(variants)\n\n**【级别】** 建议\n\n**【描述】**\n\n使用 Enum 的类型前缀可以使代码更加可读。\n\n**【反例】**\n\n```rust\n#![w"
},
{
"path": "src/safe-guides/coding_practice/data-type/enum/G.TYP.ENM.05.md",
"chars": 1072,
"preview": "## G.TYP.ENM.05 对外导出的公开Enum,宜添加`#[non_exhaustive]`属性\n\n**【级别】** 建议\n\n**【描述】**\n\n作为对外公开的 Enum,为了保持稳定性,应该使用 `#[non_exhaustive"
},
{
"path": "src/safe-guides/coding_practice/data-type/enum/G.TYP.ENM.06.md",
"chars": 930,
"preview": "## G.TYP.ENM.06 Enum内变体的大小差异不宜过大\n\n**【级别】** 建议\n\n**【描述】**\n\n要注意 Enum 内变体的大小差异不要过大,因为 Enum 内存布局是以最大的变体进行对齐。根据场景,如果该Enum 实例中"
},
{
"path": "src/safe-guides/coding_practice/data-type/enum/G.TYP.ENM.07.md",
"chars": 680,
"preview": "## G.TYP.ENM.07 如需依赖 Enum 中变体的序数,则应为变体设置明确的数值\n\n**【级别】** 要求\n\n**【描述】**\n\n在日常开发中,有时需要产生出有独立名称,且为连续或是有规律的数值,用来当作接口的参数数值,一般采用枚"
},
{
"path": "src/safe-guides/coding_practice/data-type/enum.md",
"chars": 81,
"preview": "# 枚举体\n\nRust 的枚举是一种带 Tag 的联合体。 一般分为三类:空枚举、无字段(fieldless)枚举和数据承载(data carrying)枚举。\n"
},
{
"path": "src/safe-guides/coding_practice/data-type/float/G.TYP.FLT.01.md",
"chars": 774,
"preview": "## G.TYP.FLT.01 使用浮点数字面量时,要警惕是否存在被Rust编译器截断的风险\n\n**【级别】** 建议\n\n**【描述】**\n\n当指定超过类型精度(`f32` 或 `f64`)的字面量值时,Rust 会默认截断该值。\n\n**【"
},
{
"path": "src/safe-guides/coding_practice/data-type/float/G.TYP.FLT.02.md",
"chars": 1068,
"preview": "## G.TYP.FLT.02 从任何数字类型转换为浮点类型时注意避免损失精度\n\n**【级别】** 建议\n\n**【描述】**\n\n开发者了解发生精度损失的位置,会对解决因为转换而损失精度的问题更加有好处。\n\n**【反例】**\n\n```rus"
},
{
"path": "src/safe-guides/coding_practice/data-type/float/G.TYP.FLT.03.md",
"chars": 1927,
"preview": "## G.TYP.FLT.03 对精度高要求的场景下,使用浮点数进行运算和比较时需要注意精度损失\n\n**【级别】** 建议\n\n**【描述】**\n\n浮点数计算通常都是不精确的,直接对浮点数进行运算和比较可能造成数据错误。 如何更好地处理浮点数"
},
{
"path": "src/safe-guides/coding_practice/data-type/float/G.TYP.FLT.04.md",
"chars": 1737,
"preview": "## G.TYP.FLT.04 宜使用Rust内置方法处理浮点数计算\n\n**【级别】** 建议\n\n**【描述】**\n\n内置方法可能会牺牲一定性能,但可以提升准确性。\n\n**【反例】**\n\n```rust\n#![warn(clippy::i"
},
{
"path": "src/safe-guides/coding_practice/data-type/float/G.TYP.FLT.05.md",
"chars": 982,
"preview": "## G.TYP.FLT.05 禁止在浮点数和整数相互转换时使用 `transmute`\n\n**【级别】** 要求\n\n**【描述】**\n\n使用 `transmute` 转换容易产生未定义行为,建议使用 `to_bites` 这样转换更加安全"
},
{
"path": "src/safe-guides/coding_practice/data-type/float.md",
"chars": 70,
"preview": "# 浮点数\n\nRust 的浮点数包括 `f32` 和 `f64` 两种类型。Rust 编译器默认推断的 Float 类型是 `f64`。\n\n"
},
{
"path": "src/safe-guides/coding_practice/data-type/int/G.TYP.INT.01.md",
"chars": 2212,
"preview": "## G.TYP.INT.01 在用整数计算的时候需要考虑整数溢出、回绕和截断的风险\n\n**【级别】** 建议\n\n**【描述】**\n\n如果从代码上下文的逻辑来看,该计算不可能产生溢出,则可以不进行校验。\n\n比如,对于时间要求精准的系统,如"
},
{
"path": "src/safe-guides/coding_practice/data-type/int/G.TYP.INT.02.md",
"chars": 1418,
"preview": "## G.TYP.INT.02 避免在有符号整数和无符号整数之间进行强制转换\n\n**【级别】** 建议\n\n**【描述】**\n\n当有符号整数被强制转换为无符号整数时,负值会发生回绕(wrap around),变成更大的正值,这在实际应用时有"
},
{
"path": "src/safe-guides/coding_practice/data-type/int/G.TYP.INT.03.md",
"chars": 1114,
"preview": "## G.TYP.INT.03 对负数取模计算的时候不应使用`%`\n\n**【级别】** 建议\n\n**【描述】**\n\nRust 中的 `%` 符号为余数运算符,它的行为与`C`或`Java`等语言中相同符号的运算符相同。它也类似于`Pyth"
},
{
"path": "src/safe-guides/coding_practice/data-type/int.md",
"chars": 96,
"preview": "# 整数\n\nRust 中有目前有十二种整数类型:`i8/u8`, `i16/u16`, `i32/u32`, `i64/u64`, `i128/u128`, `isize/usize` 。\n\n"
},
{
"path": "src/safe-guides/coding_practice/data-type/ref/.keep",
"chars": 0,
"preview": ""
},
{
"path": "src/safe-guides/coding_practice/data-type/ref.md",
"chars": 80,
"preview": "# 引用\n\n在 Rust 中,引用是有借用检查的指针,就像穿着“安全的外衣”。没有借用检查的指针也叫裸指针。\n\nRust 编译器总是希望引用是非空且对齐的。\n\n"
},
{
"path": "src/safe-guides/coding_practice/data-type/slice/P.TYP.SLC.01.md",
"chars": 600,
"preview": "## P.TYP.SLC.01 宜使用切片迭代器来代替手工索引\n\n**【描述】**\n\n在 for 循环中使用索引是比较常见的编程习惯,但是这种方式是最有可能导致边界错误的。\n\n利用 切片自带的方法,并利用迭代器,可以避免这种错误。\n\n\n*"
},
{
"path": "src/safe-guides/coding_practice/data-type/slice/P.TYP.SLC.02.md",
"chars": 603,
"preview": "## P.TYP.SLC.02 宜使用切片模式来提升代码的可读性\n\n**【描述】**\n\n切片也支持模式匹配,适当应用切片模式,可以有效提升代码可读性。\n\n**【正例】**\n\n利用切片模式编写判断回文字符串(如\"aba\"、\"abba\"之类)的"
},
{
"path": "src/safe-guides/coding_practice/data-type/slice.md",
"chars": 103,
"preview": "# 切片\n\n切片(slice)允许开发者引用集合中连续的元素序列,类型签名用 `[T]`表示,但因为它是动态大小类型(DST),所以一般用 `&[T]` 表示切片。\n\n`&str` 就是一种字符串切片。\n\n"
},
{
"path": "src/safe-guides/coding_practice/data-type/struct/G.TYP.SCT.01.md",
"chars": 2169,
"preview": "## G.TYP.SCT.01 对外导出的公开的 Struct,宜添加`#[non_exhaustive]`属性\n\n**【级别】** 建议\n\n**【描述】**\n\n作为对外公开的 结构体,为了保持稳定性,应该使用 `#[non_exhaust"
},
{
"path": "src/safe-guides/coding_practice/data-type/struct/G.TYP.SCT.02.md",
"chars": 1024,
"preview": "## G.TYP.SCT.02 当结构体中有超过三个布尔类型的字段,宜将其独立为新的枚举类\n\n**【级别】** 建议\n\n**【描述】**\n\n这样有助于提升 代码可读性和 API 。\n\n**【反例】**\n\n```rust\n#也适用于这里。\n\n"
},
{
"path": "src/safe-guides/coding_practice/data-type.md",
"chars": 51,
"preview": "# 3.4 数据类型\n\n数据类型指 Rust 标准库提供的 原生类型,以及结构体和枚举体等编码实践。\n"
},
{
"path": "src/safe-guides/coding_practice/error-handle/G.ERR.01.md",
"chars": 1052,
"preview": "## G.ERR.01 在处理 `Option<T>` 和 `Result<T, E>` 类型时,不要随便使用 `unwrap`\n\n**【级别】** 建议\n\n**【描述】**\n\n当 `Option<T>` 和 `Result<T, E>`"
},
{
"path": "src/safe-guides/coding_practice/error-handle/G.ERR.02.md",
"chars": 1927,
"preview": "## G.ERR.02 不要滥用 `expect`,请考虑用 `unwrap_or_` 系列方法代替\n\n**【级别】** 建议\n\n**【描述】**\n\n使用 `expect` 的时候请遵循 `expect` 的语义,不要滥用。\n\n> `"
},
{
"path": "src/safe-guides/coding_practice/error-handle/P.ERR.01.md",
"chars": 779,
"preview": "## P.ERR.01 当传入函数的参数值因为超出某种限制可能会导致函数调用失败,应该使用断言\n\n**【描述】**\n\n当传入函数的某个参数值可能因为超出某种限制,比如超出数组长度的索引、字符串是否包含某个字符、数组是否为空等,应该使用断"
},
{
"path": "src/safe-guides/coding_practice/error-handle/P.ERR.02.md",
"chars": 1392,
"preview": "## P.ERR.02 在确定 `Option<T>` 和 `Result<T, E>`类型的值不可能是 `None` 或 `Err` 时,请用 `expect` 代替 `unwrap()`\n\n**【描述】**\n\n当需要处理的 "
},
{
"path": "src/safe-guides/coding_practice/error-handle.md",
"chars": 280,
"preview": "# 3.12 错误处理\n\nRust 为了保证系统健壮性,将系统中出现的非正常情况划分为三大类:\n\n1. 失败\n2. 错误\n3. 异常\n\nRust 语言针对这三类非正常情况分别提供了专门的处理方式,让开发者可以分情况去选择。\n\n- 对于失败的"
},
{
"path": "src/safe-guides/coding_practice/expr/G.EXP.01.md",
"chars": 645,
"preview": "## G.EXP.01 当需要对表达式求值后重新赋值时,宜使用复合赋值模式\n\n**【级别】** 建议\n\n**【描述】**\n\n略\n\n**【反例】**\n\n```rust\nlet mut a = 5;\nlet b = 0;\na = a + b; "
},
{
"path": "src/safe-guides/coding_practice/expr/G.EXP.02.md",
"chars": 1295,
"preview": "## G.EXP.02 不宜在比较中使用不兼容的位掩码\n\n**【级别】** 要求\n\n**【描述】**\n\n如果比较的位总是被位掩码设置为零或一,则比较是常量true或 false(取决于掩码、比较值和运算符),这种代码是有误导性的,可能是故"
},
{
"path": "src/safe-guides/coding_practice/expr/G.EXP.03.md",
"chars": 952,
"preview": "## G.EXP.03 不应利用数组表达式的边界检查来 Panic,而应使用断言\n\n**【级别】** 建议\n\n**【描述】**\n\n这样会影响代码可读性。使用断言可以更好的描述代码的意图。\n\n**【反例】**\n\n```rust\nfn main"
},
{
"path": "src/safe-guides/coding_practice/expr/G.EXP.04.md",
"chars": 680,
"preview": "## G.EXP.04 自增或自减运算使用`+=`或`-=`\n\n**【级别】** 建议\n\n**【描述】**\n\nC/Cpp 等编程语言常用的自增自减操作,如 `++i` 、`i++` 、`i--` 等不是合法的 Rust 表达式, `--i`"
},
{
"path": "src/safe-guides/coding_practice/expr/G.EXP.05.md",
"chars": 626,
"preview": "## G.EXP.05 使用括号来清楚表示表达式的计算顺序\n\n**【级别】** 建议\n\n**【描述】**\n\n并不是每个人都能记得住优先级,所以最好使用括号把优先级顺序区分出来,增加可读性。\n\n**【反例】**\n\n```rust\n1 << 2"
},
{
"path": "src/safe-guides/coding_practice/expr/G.EXP.06.md",
"chars": 885,
"preview": "## G.EXP.06 避免在比较中添加无用的掩码操作\n\n**【级别】** 要求\n\n**【描述】**\n\n检查比较中的无用位掩码操作,可以在不改变结果的情况下删除该位掩码操作。\n\n请对照下面表格进行检查。\n\n| Comparison | Bi"
},
{
"path": "src/safe-guides/coding_practice/expr.md",
"chars": 28,
"preview": "# 3.5 表达式\n\nRust 中几乎一切皆表达式。\n\n"
},
{
"path": "src/safe-guides/coding_practice/fn-design/G.FUD.01.md",
"chars": 1330,
"preview": "## G.FUD.01 函数参数最长不要超过五个\n\n**【级别】** 建议\n\n**【描述】**\n\n为了提升代码可读性,函数的参数最长不宜超过五个。根据编译器函数调用规范[[1](https://www.cnblogs.com/shines"
},
{
"path": "src/safe-guides/coding_practice/fn-design/G.FUD.02.md",
"chars": 1076,
"preview": "## G.FUD.02 当函数参数实现了 Copy,并且是按值传入,如果值可能会太大,则宜考虑按引用传递\n\n**【级别】** 建议\n\n**【描述】**\n\n通过值传递的参数可能会导致不必要的 `memcpy` 拷贝,这可能会造成性能损失。"
},
{
"path": "src/safe-guides/coding_practice/fn-design/G.FUD.03.md",
"chars": 1095,
"preview": "## G.FUD.03 当函数参数出现太多 bool 类型的参数时,应该考虑将其封装为自定义的结构体或枚举\n\n**【级别】** 建议\n\n**【描述】**\n\n布尔类型的参数过多,很难让人记住,容易出错。将其封装为枚举或结构体,可以更好地利"
},
{
"path": "src/safe-guides/coding_practice/fn-design/G.FUD.04.md",
"chars": 2020,
"preview": "## G.FUD.04 当 Copy 类型的足够小的值作为函数参数时,应该按值(by-value)传入,而不是引用(by-ref)\n\n**【级别】** 建议\n\n**【描述】**\n\n在函数参数为 Copy 类型 且 其值足够小的时候,一般"
},
{
"path": "src/safe-guides/coding_practice/fn-design/G.FUD.05.md",
"chars": 860,
"preview": "## G.FUD.05 不要总是为函数指定 `inline(always)` \n\n**【级别】** 建议\n\n**【描述】**\n\n`inline` 虽然可以提升性能,但也会增加编译时间和编译大小。\n\nRust 中性能、编译时间和编译大小之"
},
{
"path": "src/safe-guides/coding_practice/fn-design/G.FUD.06.md",
"chars": 1475,
"preview": "## G.FUD.06 函数参数应该考虑兼容多种类型\n\n**【级别】** 建议\n\n**【描述】**\n\n这样的好处是参数可以灵活兼容更多类型,代码方便扩展。\n\n**【反例】**\n\n```rust\n// 不符合\nfn three_vowels"
},
{
"path": "src/safe-guides/coding_practice/fn-design/P.FUD.01.md",
"chars": 671,
"preview": "## P.FUD.01 传递到闭包的变量建议单独重新绑定\n\n**【描述】**\n\n默认情况下,闭包通过借用来捕获环境变量。或者,可以使用 `move` 关键字来移动环境变量到闭包中。\n\n将这些要在闭包内用的变量,重新进行分组绑定,可读性更"
},
{
"path": "src/safe-guides/coding_practice/fn-design/P.FUD.02.md",
"chars": 340,
"preview": "## P.FUD.02 函数返回值不要使用 `return`\n\n**【描述】**\n\nRust 中函数块会自动返回最后一个表达式的值,不需要显式地指定 `return`。\n\n只有在函数过程中需要提前返回的时候再加 Return。\n\n**【反"
},
{
"path": "src/safe-guides/coding_practice/fn-design.md",
"chars": 32,
"preview": "# 3.9 函数设计\n\n创建函数或使用闭包时需要注意的地方。\n\n"
},
{
"path": "src/safe-guides/coding_practice/generic/G.GEN.01.md",
"chars": 559,
"preview": "\n## G.GEN.01 不要在泛型位置上使用内建类型\n\n**【级别】** 建议\n\n**【描述】**\n\n这样做虽然会导致编译错误,但是这种错误会使开发者感到困惑,反而无法找到问题所在。\n\n**【反例】**\n\n这里 `u32` 会被认为是一个"
},
{
"path": "src/safe-guides/coding_practice/generic/G.GEN.02.md",
"chars": 1178,
"preview": "## G.GEN.02 使用 Rust 标准库中某些方法,要注意避免使用其泛型默认实现,而应该使用具体类型的实现\n\n**【级别】** 建议\n\n**【描述】**\n\nRust 标准库内部某些类型使用了 泛型特化(未稳定特性),比如 `ToS"
},
{
"path": "src/safe-guides/coding_practice/generic/P.GEN.01.md",
"chars": 1781,
"preview": "## P.GEN.01 用泛型来抽象公共语义\n\n**【描述】**\n\n应该巧用泛型来抽象公共语义,消除重复代码。\n\n**【反例】**\n\n```rust\nuse std::ops::Add;\n\n#[derive(Debug, Clone, C"
},
{
"path": "src/safe-guides/coding_practice/generic/P.GEN.02.md",
"chars": 1178,
"preview": "## P.GEN.02 不要随便使用 `impl Trait` 语法替代泛型限定\n\n**【描述】**\n\n`impl Trait` 语法 和 泛型限定,虽然都是静态分发,且效果类似,但是它们的语义是不同的。\n\n**在类型系统层面上的语义:*"
},
{
"path": "src/safe-guides/coding_practice/generic/P.GEN.03.md",
"chars": 971,
"preview": "## P.GEN.03 不要使用太多泛型参数和 trait 限定,否则会增长编译时间\n\n**【描述】**\n\n为泛型函数添加详细的 trait 限定,可以在一定程度上增强用户使用体验,但使用过多的泛型参数和 trait 限定会显著地增长编"
},
{
"path": "src/safe-guides/coding_practice/generic/P.GEN.04.md",
"chars": 2096,
"preview": "## P.GEN.04 为泛型类型实现方法时,`impl` 中声明的泛型类型参数一定要被用到\n\n**【描述】**\n\n在 `impl` 中被声明的类型参数,至少要满足下面三种形式:\n\n1. `impl<T> Foo<T>`, `T` 出"
},
{
"path": "src/safe-guides/coding_practice/generic/P.GEN.05.md",
"chars": 741,
"preview": "## P.GEN.05 定义泛型函数时,如果该函数实现用到来自 trait 定义的相关行为,需要为泛型指定相关 trait 的限定\n\n**【描述】**\n\n泛型,在 Rust 类型系统中的语义是一种 通用量化类型(Universally-qu"
},
{
"path": "src/safe-guides/coding_practice/generic.md",
"chars": 65,
"preview": "# 3.10 泛型\n\nRust 中的泛型允许开发人员编写更加简洁、更少重复的代码。但泛型可能会引起编译文件大小膨胀,酌情使用。\n\n"
},
{
"path": "src/safe-guides/coding_practice/io/G.FIO.01.md",
"chars": 1215,
"preview": "## G.FIO.01 文件读取建议使用 `BufReader/BufWriter` 来代替 `Reader/Write`\n\n**【描述】**\n\n`BufReader/BufWriter` 使用缓冲区来减少 I/O 请求的次数,提升性能。"
},
{
"path": "src/safe-guides/coding_practice/io/P.FIO.01.md",
"chars": 155,
"preview": "## P.FIO.01 使用 `read_to_end/read_to_string`方法时注意文件的大小能否一次性读入内存中\n\n**【描述】**\n\n对于内存可以一次性读完的文件,可以使用 `read_to_end/read_to_str"
},
{
"path": "src/safe-guides/coding_practice/io.md",
"chars": 206,
"preview": "# 3.21 Safe I/O\n\n在标准库中也提供了标准 I/O 类型,在 Safe Rust 下,I/O 操作是足够安全的,但是对于 原生句柄 (Raw Fd) 的操作,则属于不安全。\n\n在 Unsafe Rust 下也有相关 I/O "
},
{
"path": "src/safe-guides/coding_practice/macros/G.MAC.01.md",
"chars": 784,
"preview": "## G.MAC.01 `dbg!()` 宏只应该用于调试代码\n\n**【级别】** 建议\n\n**【描述】**\n\n`dbg!()` 宏是 Rust 内置的宏,其目的是用于调试代码。 不要将含有 dbg! 宏的代码加入到版本控制下。\n\n注意:不"
}
]
// ... and 153 more files (download for full content)
About this extraction
This page contains the full source code of the Rust-Coding-Guidelines/rust-coding-guidelines-zh GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 353 files (489.1 KB), approximately 199.2k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.