手册目录

Feng 代码风格建议

本文档用于沉淀 Feng 代码的推荐书写风格,帮助项目、团队和个人在不改变语言语义的前提下保持代码风格一致。

本文档中的内容均为建议,不是强制规范:

  • 不参与语法定义。
  • 不改变语义分析、编译器行为或运行时行为。
  • 编译器和运行时不得因是否遵循本文档中的建议而改变程序含义。
  • 用户可以基于团队协作习惯在本文档建议之上继续细化,也可以在充分理由下偏离本文档建议。

1 总体原则

  • 风格建议服务于可读性、一致性和维护成本控制,而不是制造额外语义负担。
  • 命名风格应尽量让读者一眼看出符号的大类,例如“契约、类型、成员、局部绑定、常量式全局绑定”。
  • 同一文件、同一模块、同一项目内应尽量保持一致,避免同类符号混用多套风格。

2 命名建议

2.1 spec 契约命名

  • spec 定义的契约,无论是类型契约还是函数契约,都建议使用「大驼峰」命名。
  • 推荐原因: 契约在使用层面通常承担抽象类型或抽象可调用形状的角色,与普通值和成员区分开更直观。

推荐示例:

spec CommitOptions {
  var message: i32;
}

spec ValueFormatter(value: i32): string;

2.2 type 类型与成员命名

  • type 定义的类型,建议使用「大驼峰」命名。
  • type 的字段、方法、构造函数参数、普通成员名,建议使用「小驼峰」命名。
  • 这样可以让“类型名”和“成员名”在视觉上自然分层。

推荐示例:

type UserProfile {
  let displayName: string;
  var loginCount: i32;

  func incrementLoginCount(): void {
    self.loginCount = self.loginCount + 1;
  }
}

2.3 局部 let / var 绑定命名

  • 函数、方法、代码块内部的局部 let / var 绑定,建议使用「小驼峰」命名。
  • 若变量只在很小的局部范围内使用,也建议保持可读性,避免无语义的缩写。

推荐示例:

func commit(message: string): void {
  let trimmedMessage = message;
  var retryCount = 0;
}

2.4 全局不变绑定命名

  • 顶层全局 let 绑定中,如果该值承担“类似常量”的角色,建议使用「全大写并以下划线分隔」命名。
  • 这里的重点不是“只要是 let 就一律全大写”,而是“全局、不可变、并且语义上接近常量”的绑定更适合使用该风格。

推荐示例:

let DEFAULT_PORT = 8080;
let MAX_RETRY_COUNT = 3;

不推荐把普通局部绑定写成同样风格:

func run(): void {
  let TEMP_VALUE = 1;
}

3 空行与布局建议

3.1 函数与方法之间的空行

  • 同一层级下,多个函数之间建议用一个空行分隔。
  • 同一 type 内,多个方法之间也建议用一个空行分隔。
  • 这样更利于快速扫描声明边界。

推荐示例:

func load(): void {
  sync();
}

func save(): void {
  flush();
}
type User {
  func open(): void {
    sync();
  }

  func close(): void {
    flush();
  }
}

3.2 函数或方法内部的空行

  • 函数或方法内部建议保持紧凑,通常不额外插入空行。
  • 只有在确实需要用空行表达逻辑阶段切换时,才建议少量使用。
  • 若函数本身很短,优先保持连续阅读体验。

推荐示例:

func commit(message: string): void {
  let normalizedMessage = message;
  writeLog(normalizedMessage);
  flush();
}

4 缩进与花括号建议

4.1 缩进

  • 建议统一使用「两空格」作为缩进风格。
  • 同一文件内不建议混用不同缩进宽度。
  • 同一项目若采用格式化工具,建议其默认输出与本文档保持一致。

推荐示例:

func main(args: string[]) {
  if true {
    print(args);
  }
}

4.2 花括号位置

  • 花括号块建议采用“前括号跟在上一行之后”的写法。
  • 适用于 functypeifelseforwhiletrycatchdefer 等块结构。

推荐示例:

type User {
  func commit(message: i32): i32 {
    return message;
  }
}
if ready {
  run();
} else {
  stop();
}

5 一致性建议

  • 比单条风格建议更重要的是一致性。
  • 一个项目若已经形成稳定风格,新增代码建议优先与现有代码保持一致。
  • 若团队后续提供自动格式化器,格式化器输出应视为本文档建议的工程化落地,但本文档本身仍然只是风格建议,不是语言规范。

5.1 类型后缀紧贴建议

  • 数组类型后缀 [] 建议直接紧贴前一个类型书写,不在类型与方括号之间插入空格。
  • 该建议同样适用于泛型类型实例后的数组后缀,例如建议写成 string[]Map<K, V>[],不建议写成 string []Map<K, V> []

5.2 泛型角括号紧贴建议

  • 泛型参数列表与显式类型实参列表建议直接紧贴前一个类型名、函数名或方法名书写,不在名称与 < 之间插入空格,也不在 > 前插入空格。
  • 该建议同样适用于带约束的类型参数列表与嵌套泛型,例如建议写成 Map<K: Hashable<K>, V>Box<Map<string, int>>,不建议写成 Map < K: Hashable<K>, V >Box<Map<string, int> >

6 文档注释建议

6.1 公开 API 文档注释

  • 对外公开的 API 建议始终提供紧邻声明的 /** */ 文档注释,尤其是标准库 std 与官方包中的公开声明。
  • 顶层 open typeopen specopen fitopen func 以及公开成员方法,都建议提供简洁准确的摘要说明。
  • 当参数、返回值、复杂度、零值行为或可见副作用会影响调用方理解时,建议在文档注释中补充 @param@return 或实现说明。
  • 公开 API 会显式 throw 可捕获的 Feng 异常时,使用 @throws 说明异常类型或值及触发条件。
  • 公开 API 因调用方违反前置条件而调用 runtime panic 时,使用 @panic 说明触发条件。@panic 不得写成 @throws,因为 runtime panic 不是可被 catch 的 Feng 异常。不需在每个 API 中重复列出内存分配失败、runtime 内部状态损坏等非调用方前置条件。
  • 文档注释应描述调用方需要知道的稳定语义,避免把容易过时的内部实现细节写成 API 契约。

7 与其他文档的关系

  • 本文档只描述推荐风格,不定义语法和语义。
  • 本文档只提供代码风格建议,不定义语法、语义、类型系统、函数规则、生命周期或互操作行为。