stand-odin

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Odin 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
or_else
when every failure path returns the same default:
odin
// 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
or_return
to propagate errors instead of check-and-return blocks:
odin
// 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_return
Use explicit
(value, ok)
checks only when failure modes differ (log, branch, or propagate differently per case).
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_else
odin
// 不推荐——所有分支都返回相同的 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_return
传播错误,替代检查并返回的代码块:
odin
// 不推荐写法
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
    (value, ok)
    into multi-branch control flow when every branch returns the same value — use
    or_else
  • Do not hardcode a
    0
    fallback when a
    default
    parameter is appropriate
  • Do not pre-check
    v == ""
    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
  • Do not write Go-style
    if err != nil { return err }
    ladders — use
    or_return
  • 当所有分支都返回相同值时,请勿将
    (value, ok)
    展开为多分支控制流——应使用
    or_else
  • 当适合使用
    default
    参数时,请勿硬编码
    0
    作为fallback值
  • 当空字符串解析失败会自动返回fallback值时,请勿在修剪前预先检查
    v == ""
    ——仅当调用者需要区分缺失、空值和无效值时才做区分
  • 请勿编写Go风格的
    if err != nil { return err }
    阶梯式代码——应使用
    or_return

Memory & Allocators

内存与分配器

  • context.temp_allocator
    for short-lived scratch data (env lookups, parse buffers, per-frame strings); free in bulk with
    free_all(context.temp_allocator)
    at a natural boundary (frame/request)
  • context.allocator
    for general heap allocation; pair
    make
    /
    new
    with
    delete
    /
    free
    , typically via
    defer
  • In library code, take an explicit
    allocator := context.allocator
    parameter instead of allocating from implicit globals:
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

错误处理

  • or_else
    — same default for every failure
  • or_return
    — early exit from a procedure whose final return value is an error or
    ok
    boolean
  • Explicit
    (value, ok)
    — failure modes need different handling
  • Model recoverable failures as error enums or unions returned as the last value; reserve
    panic
    for unrecoverable programmer error, never library control flow
  • assert
    /
    #assert
    only for invariants, not input validation
  • Prefer default-parameter fallbacks for config/env parsing helpers
  • or_else
    ——所有失败情况返回相同默认值
  • or_return
    ——从最终返回值为错误或
    ok
    布尔值的过程中提前退出
  • 显式
    (value, ok)
    ——失败模式需要不同处理
  • 将可恢复失败建模为错误枚举或联合类型,作为最后一个返回值;
    panic
    仅用于不可恢复的程序员错误,绝不能用于库的控制流
  • assert
    /
    #assert
    仅用于不变量检查,而非输入验证
  • 配置/环境解析辅助函数优先使用默认参数作为fallback值

Types & API Design

类型与API设计

  • snake_case
    for procedures and variables;
    Ada_Case
    for types (
    Entity_Kind
    ,
    Parse_Error
    );
    SCREAMING_SNAKE_CASE
    for constants
  • Use default parameter values for fallbacks instead of sentinel checks
  • Encode domain constraints in the name (
    parse_env_f64_nonneg
    ) or via post-parse validation (
    max(0, x)
    )
  • Prefer plain
    proc
    s and struct composition — Odin has no classes or inheritance; do not simulate them
  • 过程和变量使用
    snake_case
    命名;类型使用
    Ada_Case
    命名(如
    Entity_Kind
    Parse_Error
    );常量使用
    SCREAMING_SNAKE_CASE
    命名
  • 使用默认参数值作为fallback,而非标记值检查
  • 在名称中编码领域约束(如
    parse_env_f64_nonneg
    )或通过解析后验证实现(如
    max(0, x)
  • 优先使用普通
    proc
    和结构体组合——Odin没有类或继承;请勿模拟这些特性

Testing

测试

  • Use
    core:testing
    with the
    @(test)
    attribute; run via
    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

工具链

  • odin check
    is the primary validation gate;
    odin build -vet -strict-style
    catches unused values and style drift
  • Follow the
    lint
    skill where a project has lintro configured; no lintro Odin plugin exists yet, so the compiler vet flags are the linter
  • odin check
    是主要的验证关卡;
    odin build -vet -strict-style
    可捕获未使用的值和风格偏差
  • 若项目配置了lintro,请遵循
    lint
    技能;目前尚无Odin的lintro插件,因此编译器的vet标志即为检查工具