motoko-doc-strings

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Motoko doc strings

Motoko文档字符串

Purpose

目的

Add triple-slash (
///
) doc comments to every public declaration in Motoko source files so that
mo-doc
renders meaningful documentation pages. This skill captures the conventions used in
mo:core
(caffeinelabs/motoko-core) and the lessons learned applying them across the
motoko-bitcoin
library.
在Motoko源文件中的每个公共声明上添加三斜杠(
///
)文档注释,以便
mo-doc
渲染出有意义的文档页面。本指南涵盖了
mo:core
(caffeinelabs/motoko-core)中使用的约定,以及在
motoko-bitcoin
库应用这些约定时总结的经验。

When to Use

适用场景

  • The user asks to "add doc strings", "document the public API", or "generate docs" for a Motoko package.
  • Reviewing or polishing a library before release / publication on MOPS.
  • After adding new public symbols and noticing missing entries in the generated
    docs/
    .
  • 用户要求为Motoko包"添加文档字符串"、"记录公共API"或"生成文档"时。
  • 在发布/在MOPS上发布库之前进行审阅或优化时。
  • 添加新的公共符号后,发现生成的
    docs/
    目录中缺少对应条目时。

What Counts as a "Public Object"

什么是"公共对象"

Document every declaration that ends up in the rendered docs. In a typical
module { ... }
file these are:
  • public type ...
  • public let ...
  • public func ...
  • public class ...
  • The module itself (
    module { ... }
    or
    module Name { ... }
    )
  • For a
    public class
    , every
    public let
    ,
    public var
    , and
    public func
    member inside the class body.
  • For a nested
    public module
    , recurse and apply the same rules.
Private declarations (
func
,
let
,
class
without
public
) and helper types should NOT receive
///
comments — they don't appear in
mo-doc
output and the noise hurts readability.
为所有会出现在渲染后的文档中的声明添加文档。在典型的
module { ... }
文件中,这些声明包括:
  • public type ...
  • public let ...
  • public func ...
  • public class ...
  • 模块本身(
    module { ... }
    module Name { ... }
  • 对于
    public class
    ,类体内部的每个
    public let
    public var
    public func
    成员。
  • 对于嵌套的
    public module
    ,递归应用相同规则。
私有声明(不带
public
func
let
class
)和辅助类型不应添加
///
注释——它们不会出现在
mo-doc
的输出中,多余的注释会影响可读性。

Comprehensiveness by Location

文档详细程度规范

How thorough a doc string needs to be depends on where the file lives:
  • Inside
    src/internal/
    — doc strings can be brief. These modules are implementation details that end users are not expected to call directly, so a short one-liner stating what a declaration does is usually enough. Trap/error notes can be omitted unless the behavior is surprising to another maintainer.
  • Anywhere else under
    src/
    (i.e. outside
    src/internal/
    )
    — doc strings MUST be comprehensive. These are the public API surface that users will call, so they need every detail: argument units and formats, size/range constraints, return-value semantics, full failure behavior (
    Traps
    /
    Errors
    paragraphs), and runnable examples where helpful. Apply the full "User-Perspective Read-Through" checklist at the end of this skill to every declaration here.
文档字符串的详细程度取决于文件所在位置:
  • src/internal/
    目录内
    :文档字符串可以简洁一些。这些模块是实现细节,终端用户无需直接调用,因此用简短的单行语句说明声明的用途通常就足够了。除非行为对其他维护者来说出乎意料,否则可以省略陷阱/错误说明。
  • src/
    下的其他位置(即
    src/internal/
    之外)
    :文档字符串必须详尽。这些是用户会调用的公共API表面,因此需要包含所有细节:参数的单位和格式、大小/范围限制、返回值语义、完整的失败行为(
    Traps
    /
    Errors
    段落),以及在有帮助时提供可运行的示例。对这里的每个声明都要应用本指南末尾的"用户视角通读检查清单"。

Doc String Format

文档字符串格式

mo-doc
parses lines that start with
///
as Markdown documentation attached to the next declaration.
motoko
/// Brief one-line description ending in a period.
///
/// Optional longer paragraph(s) describing semantics, edge cases,
/// and return values. Refer to parameters by `name` in backticks.
///
/// Example:
/// ```motoko include=import
/// let result = Module.func(arg);
/// ```
public func func(arg : T) : U { ... };
Conventions used in
mo:core
:
  • First line is a short imperative summary ("Returns ...", "Computes ...", "Decodes ...") ending with a period.
  • One blank
    ///
    line separates paragraphs.
  • Wrap parameter names, types, and short code in single backticks.
  • Code blocks use
    ```motoko
    fences. Use the
    ```motoko include=import
    annotation when the snippet relies on the module's import header (defined elsewhere with
    ```motoko name=import
    ).
  • For module-level docs, define a named import block once at the top so all example snippets can reference it:
    motoko
    /// ```motoko name=import
    /// import Hash "mo:bitcoin/Hash";
    /// ```
    module { ... }
  • The
    name=import
    /
    include=import
    annotations are NOT consumed by
    mo-doc
    itself — they are directives for an external doctest runner (used by
    mo:core
    's CI and by Docusaurus-based doc sites) that prepends the named snippet before compiling each example.
    mo-doc
    passes them through verbatim into the rendered code-fence info string. If the project does not run doctests, you can omit them and just use plain
    ```motoko
    fences.
mo-doc
会将以
///
开头的行解析为附加到下一个声明的Markdown文档。
motoko
/// 简短的单行描述,以句号结尾。
///
/// 可选的较长段落,用于描述语义、边缘情况
/// 和返回值。用反引号包裹参数名称`name`来引用。
///
/// 示例:
/// ```motoko include=import
/// let result = Module.func(arg);
/// ```
public func func(arg : T) : U { ... };
mo:core
中使用的约定:
  • 第一行是简短的祈使句摘要("返回..."、"计算..."、"解码..."),以句号结尾。
  • 用一个空的
    ///
    行分隔段落。
  • 将参数名称、类型和短代码用单个反引号包裹。
  • 代码块使用
    ```motoko
    围栏。当代码片段依赖模块的导入头(在其他地方用
    ```motoko name=import
    定义)时,使用
    ```motoko include=import
    注释。
  • 对于模块级文档,在顶部定义一个命名的导入块,以便所有示例片段都可以引用它:
    motoko
    /// ```motoko name=import
    /// import Hash "mo:bitcoin/Hash";
    /// ```
    module { ... }
  • name=import
    /
    include=import
    注释不会被
    mo-doc
    本身处理——它们是外部文档测试运行器(
    mo:core
    的CI和基于Docusaurus的文档站点使用)的指令,会在编译每个示例前预先添加命名的代码片段。
    mo-doc
    会将它们原封不动地传递到渲染后的代码围栏信息字符串中。如果项目不运行文档测试,可以省略这些注释,只使用普通的
    ```motoko
    围栏。

Where to put the module-level doc string

模块级文档字符串的放置位置

The module-level
///
block must sit at the very top of the file, before the
import
statements, with one blank line separating it from the imports and one blank line between the imports and the
module { ... }
line. This matches the layout used in
dfinity/motoko-core
and is the only placement that
mo-doc
actually attaches to the module page — a doc block placed between the imports and
module { ... }
is silently ignored and the rendered module page will have no description.
motoko
/// One-line module summary.
///
/// Longer description.
///
/// ```motoko name=import
/// import Foo "mo:pkg/Foo";
/// ```

import Bar "mo:core/Bar";
import Baz "mo:core/Baz";

module {
  // ...
}
For named modules (
module Name { ... }
, e.g.
src/bitcoin/Script.mo
) the same rule applies: doc block at the top of the file, then a blank line, then imports, then a blank line, then
module Name { ... }
.
模块级的
///
块必须放在文件的最顶部,在
import
语句之前,用一个空行将其与导入语句分隔开,导入语句和
module { ... }
行之间也用一个空行分隔。这与
dfinity/motoko-core
中使用的布局一致,也是
mo-doc
会将其附加到模块页面的唯一放置方式——放在导入语句和
module { ... }
之间的文档块会被忽略,渲染后的模块页面将没有描述。
motoko
/// 单行模块摘要。
///
/// 较长的描述。
///
/// ```motoko name=import
/// import Foo "mo:pkg/Foo";
/// ```

import Bar "mo:core/Bar";
import Baz "mo:core/Baz";

module {
  // ...
}
对于命名模块(
module Name { ... }
,例如
src/bitcoin/Script.mo
),同样的规则适用:文档块放在文件顶部,然后是一个空行,接着是导入语句,再是一个空行,最后是
module Name { ... }

Where to put per-declaration comments

每个声明的注释放置位置

Place
///
lines immediately above the declaration with no blank line between them. If there is an existing legacy
// ...
comment, put the
///
block above the legacy comment (the legacy comment can stay as implementation notes).
motoko
/// Public API description goes here.
// Legacy implementation notes can stay below the doc string.
public func encode(input : [Nat8]) : Text { ... };
///
行放在声明的紧上方,两者之间没有空行。如果已有遗留的
// ...
注释,将
///
块放在遗留注释上方(遗留注释可以保留作为实现说明)。
motoko
/// 公共API描述放在这里。
// 遗留的实现说明可以保留在文档字符串下方。
public func encode(input : [Nat8]) : Text { ... };

Common Pitfalls

常见陷阱

1.
apply_patch
and literal
\n

1.
apply_patch
与字面量
\n

When inserting multi-line content, write actual newlines in the patch body (one
+
per real line). NEVER use the escape sequence
\n
inside inserted text — it will be written as the literal two characters and break the file.
插入多行内容时,在补丁正文中写入实际的换行符(每行一个
+
)。绝对不要在插入的文本中使用转义序列
\n
——它会被写成两个字面字符并破坏文件。

2. Don't accidentally insert into the middle of a function

2. 不要意外插入到函数内部

apply_patch
matches on context. When the surrounding context is too short or appears multiple times, the inserted block may land inside a function body. After inserting docs, run a quick syntax check (e.g.
mo-doc
or
moc --check
) to catch this. A telltale sign is a
syntax error [M0001], unexpected token 'import'
message — that means a new module-level block was placed inside a
loop
/
func
body.
apply_patch
会根据上下文匹配。当周围的上下文太短或出现多次时,插入的块可能会落在函数体内部。插入文档后,运行快速语法检查(例如
mo-doc
moc --check
)来发现此类问题。一个明显的迹象是出现
syntax error [M0001], unexpected token 'import'
消息——这意味着新的模块级块被放在了
loop
/
func
体内部。

3. Class member documentation order

3. 类成员文档的顺序

mo-doc
renders class fields and methods in source order, but the class's own description block is shown after the member list when the class doc appears above the constructor. To keep the class summary at the top of the rendered class page, place the
///
block on the line directly preceding
public class Name(...)
.
mo-doc
会按照源代码顺序渲染类的字段和方法,但如果类的描述块放在构造函数上方,类的描述会在成员列表之后显示。要让类摘要显示在渲染后的类页面顶部,需将
///
块放在
public class Name(...)
的紧上方。

4. Re-exported types

4. 重导出的类型

Re-exported types like
public type Signature = Types.Signature;
still need a one-line
///
description so they are not rendered as "(no description)".
public type Signature = Types.Signature;
这样的重导出类型仍然需要一行
///
描述,以免被渲染为"(无描述)"。

5. Skip private helpers and constants

5. 跳过私有辅助函数和常量

Don't add
///
to
let
,
func
, or
type
declarations that lack
public
. They never appear in the output and the comments add visual noise.
不要给不带
public
let
func
type
声明添加
///
注释。它们永远不会出现在输出中,注释只会增加视觉噪音。

6.
module Name { ... }
named modules

6.
module Name { ... }
命名模块

Files like
src/bitcoin/Script.mo
use the form
module Script { ... }
instead of the bare
module { ... }
. The same top-of-file placement rule applies — the module-level
///
block goes at the very start of the file (before the imports), not on the line directly preceding
module Script {
.
src/bitcoin/Script.mo
这样的文件使用
module Script { ... }
形式,而不是裸
module { ... }
。同样的顶部放置规则适用——模块级的
///
块放在文件的最开头(在导入语句之前),而不是
module Script {
的紧上方。

7. Module doc must be at the top of the file

7. 模块文档必须放在文件顶部

mo-doc
only treats a
///
block as the module description when it appears at the very beginning of the file, ahead of the
import
statements. A doc block placed between the imports and
module { ... }
compiles fine but produces an empty module description in the rendered HTML. If you find an existing project with module docs adjacent to
module { ... }
, relocate them to the top of the file (a small Python script that finds the trailing
///
block before
module 
and prepends it to the file works well for batch migration).
只有当
///
块出现在文件的最开头、在
import
语句之前时,
mo-doc
才会将其视为模块描述。放在导入语句和
module { ... }
之间的文档块可以正常编译,但在渲染后的HTML中会产生空的模块描述。如果发现现有项目的模块文档紧邻
module { ... }
,请将它们移到文件顶部(一个小型Python脚本可以批量迁移,它会找到
module 
之前的末尾
///
块并将其前置到文件中)。

Workflow

工作流

  1. Inventory public declarations:
    bash
    grep -RInE "public (type|func|class|let)" src
  2. For each
    .mo
    file:
    • Add a module-level
      ///
      block (with a
      name=import
      example) at the beginning of the file, even before the block of import statements.
    • Add
      ///
      blocks above every public declaration inside the top-level modules.
    • Recurse into nested public declarations. For instance public members of public classes need doc strings. Public members of public modules need doc strings. And so on.
  3. Re-scan to catch anything missed:
    bash
    awk '
      FNR==1{prev=""}
      {
        if ($0 ~ /^[[:space:]]*public[[:space:]]+(type|func|class|let)\b/) {
          p=prev; gsub(/^[[:space:]]+|[[:space:]]+$/, "", p);
          if (p !~ /^\/{3}/) printf "%s:%d:%s\n", FILENAME, FNR, $0;
        }
        if ($0 !~ /^[[:space:]]*$/) prev=$0;
      }
    ' src/*.mo src/**/*.mo
    Empty output = all public declarations are preceded by a
    ///
    line.
  4. Generate docs:
    bash
    mo-doc --source src --output docs --format html
    No output = success. Any "Skipping ..." line indicates a syntax error that must be fixed (often a stray
    apply_patch
    corruption).
  5. Spot-check the rendered output:
    • docs/index.html
      — every module should appear in the listing.
    • Each
      docs/<Module>.html
      — module description, types, functions, and class members should all show their text.
  1. 盘点公共声明:
    bash
    grep -RInE "public (type|func|class|let)" src
  2. 对于每个
    .mo
    文件:
    • 在文件开头(甚至在导入语句块之前)添加模块级的
      ///
      块(包含
      name=import
      示例)。
    • 在顶层模块内的每个公共声明上方添加
      ///
      块。
    • 递归处理嵌套的公共声明。例如,公共类的公共成员需要文档字符串,公共模块的公共成员也需要文档字符串,依此类推。
  3. 重新扫描以遗漏的内容:
    bash
    awk '
      FNR==1{prev=""}
      {
        if ($0 ~ /^[[:space:]]*public[[:space:]]+(type|func|class|let)\b/) {
          p=prev; gsub(/^[[:space:]]+|[[:space:]]+$/, "", p);
          if (p !~ /^\/{3}/) printf "%s:%d:%s\n", FILENAME, FNR, $0;
        }
        if ($0 !~ /^[[:space:]]*$/) prev=$0;
      }
    ' src/*.mo src/**/*.mo
    输出为空表示所有公共声明前都有
    ///
    行。
  4. 生成文档:
    bash
    mo-doc --source src --output docs --format html
    无输出表示成功。任何"Skipping ..."行表示存在必须修复的语法错误(通常是
    apply_patch
    导致的损坏)。
  5. 抽查渲染后的输出:
    • docs/index.html
      ——每个模块都应出现在列表中。
    • 每个
      docs/<Module>.html
      ——模块描述、类型、函数和类成员都应显示对应的文本。

Tips for Writing Useful Descriptions

编写实用描述的技巧

  • Describe what the function does and what it returns, not how it is implemented.
  • For low-level helpers (
    readBE32
    ,
    writeLE64
    , etc.) a one-liner stating the byte order, width, and offset semantics is sufficient.
  • For domain types (
    SighashType
    ,
    WitnessProgram
    ,
    OutPoint
    ) name the spec or BIP that defines the format and link to it.
  • Keep examples short and self-contained; prefer literal byte arrays over reading from external sources.
  • 描述函数做什么以及返回什么,而不是它是如何实现的。
  • 对于低级辅助函数(
    readBE32
    writeLE64
    等),用单行语句说明字节顺序、宽度和偏移语义就足够了。
  • 对于领域类型(
    SighashType
    WitnessProgram
    OutPoint
    ),注明定义格式的规范或BIP并提供链接。
  • 示例要简短且自包含;优先使用字面字节数组,而不是从外部源读取。

Documenting Error and Trap Behavior (REQUIRED)

记录错误和陷阱行为(必填)

Every public function doc string MUST describe its full failure behavior. Readers cannot tell from a type signature alone whether a function traps, returns
null
, returns
#err
, or simply produces a wrong-but-defined result on bad input — the doc string is the only place this contract is recorded.
每个公共函数的文档字符串必须描述其完整的失败行为。读者无法仅从类型签名判断函数是否会触发陷阱、返回
null
、返回
#err
,还是在输入错误时产生错误但已定义的结果——文档字符串是记录此约定的唯一位置。

What to look for in the implementation

实现中需要注意的点

Scan the function body (and every helper it calls) for:
  • Runtime.trap(...)
    /
    Debug.trap(...)
    /
    Prim.trap(...)
    calls.
  • assert ...;
    statements (a failed assert traps).
  • Pattern matches that are non-exhaustive in practice (e.g. a
    switch
    on
    ?T
    whose
    null
    branch traps, or a
    case (#err _) Runtime.trap ...
    ).
  • Implicit traps from the standard library: out-of-bounds array indexing (
    a[i]
    when
    i >= a.size()
    ),
    Nat
    subtraction underflow, division by zero,
    Option.unwrap
    on
    null
    .
  • Explicit error returns:
    ?T
    returning
    null
    ,
    Result<T, E>
    returning
    #err
    , variant returns like
    { #ok; #err }
    .
扫描函数体(以及它调用的每个辅助函数),查找:
  • Runtime.trap(...)
    /
    Debug.trap(...)
    /
    Prim.trap(...)
    调用。
  • assert ...;
    语句(断言失败会触发陷阱)。
  • 实际非穷尽的模式匹配(例如对
    ?T
    switch
    ,其
    null
    分支触发陷阱,或
    case (#err _) Runtime.trap ...
    )。
  • 标准库的隐式陷阱:数组索引越界(
    i >= a.size()
    时的
    a[i]
    )、
    Nat
    减法下溢、除以零、
    Option.unwrap
    处理
    null
  • 显式错误返回:
    ?T
    返回
    null
    Result<T, E>
    返回
    #err
    、变体返回如
    { #ok; #err }

What to write

需要编写的内容

For each failure mode, state in the doc:
  1. The condition — what input or state triggers it, in user-facing terms ("when
    input
    contains a character outside the Base58 alphabet", not "when
    mapBase58[c] == 255
    ").
  2. The outcome
    traps
    ,
    returns null
    ,
    returns #err(...)
    , etc.
  3. For
    Result
    /option returns
    , list every distinct error case separately when the variants carry meaning.
Use a dedicated
Traps
and/or
Errors
paragraph (or both) at the end of the doc, after the example and before the runtime/space notes:
motoko
/// Decodes a Base58-encoded string into the original byte array.
///
/// ```motoko include=import
/// let bytes = Base58.decode("StV1DL6CwTryKyV");
/// ```
///
/// Traps if `encoded` contains any character that is not in the Base58
/// alphabet (i.e. not in
/// `123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz`).
public func decode(encoded : Text) : [Nat8] { ... };
For graceful errors:
motoko
/// Parses a WIF-encoded private key.
///
/// Returns `#err(msg)` when:
/// - the input is not valid Base58Check (bad checksum or alphabet),
/// - the decoded payload has an unexpected length (must be 33 or 34 bytes),
/// - the version byte does not match `network`.
public func decode(wif : Text, network : Network) : Result<PrivateKey, Text> { ... };
Use the wording "Traps" (capitalised, present tense) for unrecoverable failures so it stands out and is greppable across the codebase.
对于每种失败模式,在文档中说明:
  1. 触发条件——什么输入或状态会触发它,用用户易懂的术语描述(例如"当
    input
    包含Base58字母表之外的字符时",而不是"当
    mapBase58[c] == 255
    时")。
  2. 结果——
    traps
    returns null
    returns #err(...)
    等。
  3. 对于
    Result
    /可选返回
    ,当变体带有特定含义时,分别列出每个不同的错误情况。
在文档末尾使用专门的
Traps
和/或
Errors
段落(或两者都用),放在示例之后、运行时/空间说明之前:
motoko
/// 将Base58编码的字符串解码为原始字节数组。
///
/// ```motoko include=import
/// let bytes = Base58.decode("StV1DL6CwTryKyV");
/// ```
///
/// 当`encoded`包含Base58字母表(即不在
/// `123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz`中)之外的任何字符时,会触发陷阱。
public func decode(encoded : Text) : [Nat8] { ... };
对于优雅错误处理:
motoko
/// 解析WIF编码的私钥。
///
/// 当以下情况发生时返回`#err(msg)`:
/// - 输入不是有效的Base58Check(校验和错误或字母表错误),
/// - 解码后的负载长度不符合预期(必须为33或34字节),
/// - 版本字节与`network`不匹配。
public func decode(wif : Text, network : Network) : Result<PrivateKey, Text> { ... };
对于不可恢复的失败,使用**"Traps"**(大写,一般现在时)的措辞,使其突出显示并可在整个代码库中通过grep查找。

Pure / total functions

纯函数/全函数

If a function genuinely cannot fail (e.g.
Array.size
, a pure arithmetic helper that uses fixed-width types and total operators), say so explicitly with a one-liner like "Never traps." or omit the failure section entirely — but only after auditing the body to confirm.
如果函数确实不会失败(例如
Array.size
、使用固定宽度类型和全运算符的纯算术辅助函数),可以明确用单行语句说明"Never traps.",或者完全省略失败部分——但只有在审核函数体确认后才能这样做。

Worked examples from
motoko-bitcoin

motoko-bitcoin
中的示例

  • Base58.decode
    — traps on any character outside the alphabet (not a
    Result
    , no graceful fallback).
  • Base58Check.decode
    — returns
    ?[Nat8]
    ;
    null
    on bad alphabet, bad length, or checksum mismatch. Document each.
  • Bech32.decode
    — returns
    Result<_, Text>
    with distinct messages for invalid characters, mixed case, bad checksum, length out of range, invalid HRP. Mention each error category, not just "returns
    #err
    ".
  • Bip32.derivePath
    — traps on hardened derivation from a public key.
  • Fp.inverse
    (and the
    /
    operator) — traps when the value is zero.
  • Affine.add
    /
    Jacobi.add
    — document behaviour at the point at infinity and for equal/opposite inputs.
  • Transaction serialization (
    Transaction.toBytes
    ,
    TxInput.toBytes
    , etc.) — note any size limits that would cause
    Nat32
    /
    Nat64
    conversion traps.
  • Base58.decode
    ——当输入包含字母表之外的任何字符时触发陷阱(不是
    Result
    ,没有优雅回退)。
  • Base58Check.decode
    ——返回
    ?[Nat8]
    ;当字母表错误、长度错误或校验和不匹配时返回
    null
    。需分别记录每种情况。
  • Bech32.decode
    ——返回
    Result<_, Text>
    ,针对无效字符、大小写混合、校验和错误、长度超出范围、无效HRP等情况返回不同的消息。需提及每个错误类别,而不仅仅是"返回
    #err
    "。
  • Bip32.derivePath
    ——从公钥进行硬派生时触发陷阱。
  • Fp.inverse
    (以及
    /
    运算符)——当值为零时触发陷阱。
  • Affine.add
    /
    Jacobi.add
    ——记录无穷远点以及相等/相反输入时的行为。
  • 交易序列化(
    Transaction.toBytes
    TxInput.toBytes
    等)——记录可能导致
    Nat32
    /
    Nat64
    转换陷阱的任何大小限制。

Audit workflow for an existing file

现有文件的审核工作流

  1. List every
    public func
    and
    public class
    member.
  2. For each, read the body and follow the call graph one level into private helpers.
  3. Note every
    trap
    ,
    assert
    ,
    Result
    /
    Option
    return, and any implicit trap source (subtraction, indexing, division).
  4. Update the doc string to enumerate the conditions.
  5. Re-run
    mo-doc
    and visually scan the rendered HTML for sections that still lack a "Traps" / "Errors" paragraph on a non-trivial function.
  1. 列出每个
    public func
    public class
    成员。
  2. 对于每个成员,阅读函数体并跟踪调用图到私有辅助函数的第一层。
  3. 记录每个
    trap
    assert
    Result
    /
    Option
    返回,以及任何隐式陷阱源(减法、索引、除法)。
  4. 更新文档字符串以枚举这些条件。
  5. 重新运行
    mo-doc
    并目视扫描渲染后的HTML,查找非平凡函数仍缺少"Traps"/"Errors"段落的部分。

Final Step: User-Perspective Read-Through (REQUIRED)

最后一步:用户视角通读检查(必填)

After every public declaration has a doc string, do one more pass. Read each doc string from the perspective of a first-time user of the API who has not seen the implementation. For every doc, ask:
  • What is the unit / format of each argument and the return value? (bytes vs. bits, big- vs. little-endian, satoshis vs. BTC, raw vs. DER-encoded, compressed vs. uncompressed, 0-based vs. 1-based, …)
  • What are the size or range constraints on each input?
  • Which BIP / RFC / spec defines the format, and is it linked?
  • For mutating methods, what state changes? Is the receiver still usable afterward?
  • For functions that take a callback or proxy (e.g. an ECDSA signer), what is the expected input/output shape of the callback?
  • For variant returns, what does each variant mean semantically (not just what tag it carries)?
  • Are domain-specific terms ("witness program", "tap leaf", "sighash", "scriptPubKey") used without a one-line explanation or link?
  • For constants (
    EMPTY_WITNESS
    ,
    dustThreshold
    ), what is the value and why does it have the value it has?
  • For re-exported types, where is the actual definition (and is the link there)?
  • If you removed the function name, would the description still be unambiguous? If two near-identical functions exist (e.g.
    toBytes
    vs.
    toBytesIgnoringWitness
    ,
    encode
    vs.
    encodeUncompressed
    ), is the difference between them spelled out?
If any question remains unanswered, extend the doc string to address it. Prefer one extra sentence in the doc over forcing the user to read the source. Do this pass file by file; it usually surfaces 2–5 missing facts per non-trivial module.
在每个公共声明都有文档字符串后,再做一次检查。从首次使用API且未见过实现的用户视角阅读每个文档字符串。对于每个文档,问自己:
  • 每个参数和返回值的单位/格式是什么?(字节vs位、大端vs小端、聪vsBTC、原始vsDER编码、压缩vs未压缩、0基vs1基等)
  • 每个输入的大小或范围限制是什么?
  • 定义格式的BIP/RFC/规范是什么,是否提供了链接?
  • 对于可变方法,状态会发生什么变化?接收器之后是否仍可使用?
  • 对于接受回调或代理(例如ECDSA签名器)的函数,回调的预期输入/输出形状是什么?
  • 对于变体返回,每个变体在语义上是什么意思(不仅仅是它携带的标签)?
  • 是否使用了领域特定术语("见证程序"、"tap leaf"、"签名哈希"、"scriptPubKey")但没有单行解释或链接?
  • 对于常量(
    EMPTY_WITNESS
    dustThreshold
    ),其值是什么,为什么是这个值?
  • 对于重导出的类型,实际定义在哪里(是否有链接)?
  • 如果去掉函数名,描述是否仍然明确?如果存在两个几乎相同的函数(例如
    toBytes
    vs
    toBytesIgnoringWitness
    encode
    vs
    encodeUncompressed
    ),是否明确说明了它们之间的区别?
如果有任何问题未得到解答,请扩展文档字符串以解决它。宁愿在文档中多写一句话,也不要强迫用户阅读源代码。逐个文件进行此检查;对于非平凡模块,通常会发现2-5个缺失的信息点。