stand-odin
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseOdin Standards
Odin编码标准
Standards for Odin code. Odin is underrepresented in training data — do not
transplant Go/Rust/C error-handling patterns verbatim; reach for Odin
constructs first.
Odin代码编写规范。Odin在训练数据中的代表性不足——请勿直接照搬Go/Rust/C的错误处理模式;优先使用Odin自身的语法结构。
Idioms
惯用写法
Prefer when every failure path returns the same default:
or_elseodin
// Don't — all branches return the same fallback
parse_env_int :: proc(key: string, default: int = 0) -> int {
v, ok := os.lookup_env(key, context.temp_allocator)
if !ok || v == "" { return default }
x, ok2 := strconv.parse_int(strings.trim_space(v), 10)
if !ok2 { return default }
return x
}
// Do
parse_env_int :: proc(key: string, default: int = 0) -> int {
v := os.lookup_env(key, context.temp_allocator) or_else ""
return strconv.parse_int(strings.trim_space(v), 10) or_else default
}Domain validation after parse is one line, not a rewrite:
odin
parse_env_f64_nonneg :: proc(key: string, default: f64 = 0) -> f64 {
v := os.lookup_env(key, context.temp_allocator) or_else ""
x := strconv.parse_f64(strings.trim_space(v)) or_else default
return max(0, x)
}Use to propagate errors instead of check-and-return blocks:
or_returnodin
// Don't
data, err := os.read_entire_file_or_err(path)
if err != nil { return nil, err }
// Do
data := os.read_entire_file_or_err(path) or_returnUse explicit checks only when failure modes differ (log,
branch, or propagate differently per case).
(value, ok)Mark file-local helpers and must-use results with attributes:
odin
@(private = "file")
trimmed :: proc(s: string) -> string { return strings.trim_space(s) }
@(require_results)
checksum :: proc(data: []byte) -> u32 { ... }当所有失败路径都返回相同默认值时,优先使用:
or_elseodin
// 不推荐——所有分支都返回相同的 fallback 值
parse_env_int :: proc(key: string, default: int = 0) -> int {
v, ok := os.lookup_env(key, context.temp_allocator)
if !ok || v == "" { return default }
x, ok2 := strconv.parse_int(strings.trim_space(v), 10)
if !ok2 { return default }
return x
}
// 推荐写法
parse_env_int :: proc(key: string, default: int = 0) -> int {
v := os.lookup_env(key, context.temp_allocator) or_else ""
return strconv.parse_int(strings.trim_space(v), 10) or_else default
}解析后的领域验证只需一行代码,无需重写逻辑:
odin
parse_env_f64_nonneg :: proc(key: string, default: f64 = 0) -> f64 {
v := os.lookup_env(key, context.temp_allocator) or_else ""
x := strconv.parse_f64(strings.trim_space(v)) or_else default
return max(0, x)
}使用传播错误,替代检查并返回的代码块:
or_returnodin
// 不推荐写法
data, err := os.read_entire_file_or_err(path)
if err != nil { return nil, err }
// 推荐写法
data := os.read_entire_file_or_err(path) or_return仅当失败模式不同(如日志记录、分支处理或按不同情况传播错误)时,才使用显式的检查。
(value, ok)使用属性标记文件本地辅助函数和必须使用返回值的函数:
odin
@(private = "file")
trimmed :: proc(s: string) -> string { return strings.trim_space(s) }
@(require_results)
checksum :: proc(data: []byte) -> u32 { ... }Anti-Patterns
反模式
- Do not expand into multi-branch control flow when every branch returns the same value — use
(value, ok)or_else - Do not hardcode a fallback when a
0parameter is appropriatedefault - Do not pre-check before trimming when a failed parse of the empty string already yields the fallback — only distinguish missing vs empty vs invalid when the caller needs the distinction
v == "" - Do not write Go-style ladders — use
if err != nil { return err }or_return
- 当所有分支都返回相同值时,请勿将展开为多分支控制流——应使用
(value, ok)or_else - 当适合使用参数时,请勿硬编码
default作为fallback值0 - 当空字符串解析失败会自动返回fallback值时,请勿在修剪前预先检查——仅当调用者需要区分缺失、空值和无效值时才做区分
v == "" - 请勿编写Go风格的阶梯式代码——应使用
if err != nil { return err }or_return
Memory & Allocators
内存与分配器
- for short-lived scratch data (env lookups, parse buffers, per-frame strings); free in bulk with
context.temp_allocatorat a natural boundary (frame/request)free_all(context.temp_allocator) - for general heap allocation; pair
context.allocator/makewithnew/delete, typically viafreedefer - In library code, take an explicit parameter instead of allocating from implicit globals:
allocator := context.allocator
odin
// Don't — caller cannot control allocation
clone_name :: proc(s: string) -> string { return strings.clone(s) }
// Do — caller controls allocation; allocator errors propagate
clone_name :: proc(s: string, allocator := context.allocator) -> (string, mem.Allocator_Error) {
return strings.clone(s, allocator)
}- Copy strings that must outlive their source; temp-allocate strings that do not escape the current scope
- 用于短期临时数据(环境变量查找、解析缓冲区、每帧字符串);在合适的边界(帧/请求)处使用
context.temp_allocator批量释放free_all(context.temp_allocator) - 用于通用堆内存分配;将
context.allocator/make与new/delete配对使用,通常通过free实现defer - 在库代码中,接受显式的参数,而非从隐式全局变量分配内存:
allocator := context.allocator
odin
// 不推荐——调用者无法控制分配
clone_name :: proc(s: string) -> string { return strings.clone(s) }
// 推荐写法——调用者可控制分配;分配器错误会被传播
clone_name :: proc(s: string, allocator := context.allocator) -> (string, mem.Allocator_Error) {
return strings.clone(s, allocator)
}- 复制必须比源对象存活更久的字符串;临时分配不会逃逸出当前作用域的字符串
Error Handling
错误处理
- — same default for every failure
or_else - — early exit from a procedure whose final return value is an error or
or_returnbooleanok - Explicit — failure modes need different handling
(value, ok) - Model recoverable failures as error enums or unions returned as the last
value; reserve for unrecoverable programmer error, never library control flow
panic - /
assertonly for invariants, not input validation#assert - Prefer default-parameter fallbacks for config/env parsing helpers
- ——所有失败情况返回相同默认值
or_else - ——从最终返回值为错误或
or_return布尔值的过程中提前退出ok - 显式——失败模式需要不同处理
(value, ok) - 将可恢复失败建模为错误枚举或联合类型,作为最后一个返回值;仅用于不可恢复的程序员错误,绝不能用于库的控制流
panic - /
assert仅用于不变量检查,而非输入验证#assert - 配置/环境解析辅助函数优先使用默认参数作为fallback值
Types & API Design
类型与API设计
- for procedures and variables;
snake_casefor types (Ada_Case,Entity_Kind);Parse_Errorfor constantsSCREAMING_SNAKE_CASE - Use default parameter values for fallbacks instead of sentinel checks
- Encode domain constraints in the name () or via post-parse validation (
parse_env_f64_nonneg)max(0, x) - Prefer plain s and struct composition — Odin has no classes or inheritance; do not simulate them
proc
- 过程和变量使用命名;类型使用
snake_case命名(如Ada_Case、Entity_Kind);常量使用Parse_Error命名SCREAMING_SNAKE_CASE - 使用默认参数值作为fallback,而非标记值检查
- 在名称中编码领域约束(如)或通过解析后验证实现(如
parse_env_f64_nonneg)max(0, x) - 优先使用普通和结构体组合——Odin没有类或继承;请勿模拟这些特性
proc
Testing
测试
- Use with the
core:testingattribute; run via@(test)odin test - Table-driven cases over one-proc-per-case:
odin
import "core:testing"
@(test)
parse_env_int_defaults :: proc(t: ^testing.T) {
cases := [][2]int{{-3, -3}, {0, 0}, {42, 42}}
for c in cases {
testing.expect_value(t, parse_env_int("MISSING_KEY", c[0]), c[1])
}
}- For parsing helpers, cover: missing, empty, whitespace-only, invalid, valid, and domain edges (e.g. negative clamp)
- Defer global coverage/commit rules to
stand-general
- 使用并配合
core:testing属性;通过@(test)运行测试odin test - 优先使用表格驱动测试用例,而非每个用例对应一个过程:
odin
import "core:testing"
@(test)
parse_env_int_defaults :: proc(t: ^testing.T) {
cases := [][2]int{{-3, -3}, {0, 0}, {42, 42}}
for c in cases {
testing.expect_value(t, parse_env_int("MISSING_KEY", c[0]), c[1])
}
}- 对于解析辅助函数,需覆盖:缺失值、空值、仅含空白字符、无效值、有效值以及领域边界情况(如负数截断)
- 全局覆盖率/提交规则遵循
stand-general
Toolchain
工具链
- is the primary validation gate;
odin checkcatches unused values and style driftodin build -vet -strict-style - Follow the skill where a project has lintro configured; no lintro Odin plugin exists yet, so the compiler vet flags are the linter
lint
- 是主要的验证关卡;
odin check可捕获未使用的值和风格偏差odin build -vet -strict-style - 若项目配置了lintro,请遵循技能;目前尚无Odin的lintro插件,因此编译器的vet标志即为检查工具
lint