Skip to main content

v-lang

The V language rules that make code which looks right fail to compile or behave wrongly - ?T versus !T, sum types and match exhaustiveness, mut receivers, module names matching directories, comptime $ forms, and explicit type conversion. Use when writing or reviewing any V code that uses an Option, a Result, a sum type, a match, generics, or a custom flag, and when a V compiler error names an option, a result, exhaustiveness, mutability, or a module. Does not cover the build and test loop (see v-workflow), writing tests (see v-testing), reading a V project through the MCP server (see v-mcp), scripting in V (see v-scripts), or the wider command surface (see v-tools).

소스 정보

저장소
vlang/v
최근 소스 활동
2026년 10월 4일 06:11
감지된 SKILL.md 언어
영어
스타
38,011
포크
2,302

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

파일 탐색기
5 개 파일

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
v-lang
description
The V language rules that make code which looks right fail to compile or behave wrongly - ?T versus !T, sum types and match exhaustiveness, mut receivers, module names matching directories, comptime $ forms, and explicit type conversion. Use when writing or reviewing any V code that uses an Option, a Result, a sum type, a match, generics, or a custom flag, and when a V compiler error names an option, a result, exhaustiveness, mutability, or a module. Does not cover the build and test loop (see v-workflow), writing tests (see v-testing), reading a V project through the MCP server (see v-mcp), scripting in V (see v-scripts), or the wider command surface (see v-tools).
license
MIT
# The V rules that bite Most mistakes an agent makes in V are not algorithmic. They are these rules, and each one fails in a way that does not look like the mistake. ## Resource Routing - `references/OPTION-RESULT.md` - Read when deciding whether a function returns `?T` or `!T`, or when an `or {}` block is not doing what you expected. - `references/SUMTYPES.md` - Read when modelling a closed set of states, or when `match` reports a non-exhaustive match. - `references/COMPTIME.md` - Read when a symbol only exists on some platforms, or when reaching for reflection or `$embed_file`. - `references/MUTABILITY.md` - Read when an assignment is rejected, or when a mutation to a field does not reach the caller. ## Quick Reference | You want | Write | Not | | --- | --- | --- | | A value that may be absent | `fn f() ?int` | `return 0` as a sentinel | | An operation that can fail | `fn f() !int` | `return -1` | | Unwrap, no default | `x or { return error('...') }` | `x or { 0 }` on a result | | Unwrap, with a default | `x or { 0 }` on an option | `x or { return }` on an option | | Branch on a closed set | `enum` + exhaustive `match` | a string constant | | Mutate an argument | `fn f(mut x T)` | `x = ...` silently failing | | Mutate the caller's struct | `fn (mut t &T)` | `fn (mut t T)` (a copy) | | Run code at compile time | `$if`, `$for` | a runtime `if` | ## `?T` is not `!T` `?T` holds a value or `none`. `!T` holds a value or an error. They unwrap differently, and using one where the other belongs is a type error rather than a logic error. ```v ignore // An option: absence is part of the answer. fn find_port(cfg Config) ?int { if !cfg.has_port { return none } return cfg.port } // A result: the operation can fail, and the failure carries why. fn read_port(path string) !int { lines := os.read_lines(path) or { return error('cannot read ${path}: ${err.msg()}') } ... } ``` The rule that catches people: inside `or { ... }` on a **result**, the block must produce that result's error value, so it is `return error(...)` and not a bare `return`. On an **option**, `or { default }` supplies a value. **Default**: return `!T` for anything that touches the outside world, and `?T` for a lookup that is legitimately absent. Full detail, including how to propagate and when a default is honest, in `references/OPTION-RESULT.md`. ## Sum types and match V enums plus `match` are the way to model a closed set. `match` over an enum is checked for exhaustiveness, which is the point: adding a variant turns every incomplete `match` into a compile error instead of a silent fallthrough. The diagnostic is exact: `non-exhaustive match expression without `else``. Two ways out — handle every variant, or add an `else` branch and accept that a new variant changes nothing. ```v ignore enum Status { pending running done failed } fn label(s Status) string { return match s { .pending { 'queued' } .running { 'in progress' } .done { 'finished' } .failed { 'could not finish' } } } ``` Variants are shared: `type Job = Status | string` is a sum type, and a `match` over it must handle both sides. See `references/SUMTYPES.md`. ## mut is not optional Function arguments are immutable. A receiver is immutable unless it is declared `mut`. This is the single most common V compile error, and it always has the same cause: the author assumed mutation was the default. ```v ignore // Rejected: `app` is immutable. // fn serve(app App) { app.port = 8080 } // Accepted: the argument is mutable. fn serve(mut app App) { app.port = 8080 } // Accepted, and it reaches the caller: the receiver is a pointer. fn serve(app &App) { app.port = 8080 } // Accepted: the field itself is declared mutable. struct Server { mut: port int } ``` `mut` on a **value** receiver mutates a copy, so the caller's struct is unchanged. `mut` on a **pointer** receiver mutates the caller's. That difference is silent at the call site, which is why `references/MUTABILITY.md` exists. ## Module names must match directories `module foo` must live in a directory named `foo`. A mismatch imports cleanly and then resolves to nothing, with no error pointing at the name. The `module` line carries no hierarchy — the directory nesting supplies it. This is why a project built by `v new` has `src/main.v` with `module main` rather than `module src`. ## Comptime is `$`, and it excludes branches Anything starting with `$` runs while the compiler runs. The branches of an `$if` are **excluded entirely** on a platform that does not match, which is the only reason a platform-specific import inside one is safe. ```v ignore $if windows { import sys.windows as wsys } $if linux { import sys.linux } ``` A runtime `if` that mentions a platform-specific symbol will not compile on the other platforms, because there is no exclusion. Available in `$if`: `windows`, `linux`, `macos`, `js`, `freebsd`, `android`, `debug`, `prod`, and your own `$d custom_flag ? { ... }` — note the `?`. ## No implicit conversions `int` is 32 bit and `i64` is 64. `f64` and `int` do not mix. Nothing converts implicitly, which is deliberate: a silent narrowing is a bug you find later. Be explicit at the boundary, and prefer `strconv` over hand-rolled parsing. ## Validation After changing V code, prove it rather than reading it again: ```bash v -check path/to/file.v # type-check only, no binary v fmt -verify path/to/file.v # would the formatter change it? ``` For library code rather than a `main` module, `-check` alone fails with *"project must include a `main` module"*; use `v -check -shared path/to/file.v`. Run both before you report the change done. See `v-workflow` for the full loop. ## Related Skills - **The build and test loop**: see [v-workflow](../v-workflow/SKILL.md) for `v.mod`, dependencies, when `./v self` is required, and the flags that go before the subcommand. - **Writing tests**: see [v-testing](../v-testing/SKILL.md) when the change needs a `_test.v` file, or when `?T` and `!T` need asserting. - **Asking the compiler**: see [v-mcp](../v-mcp/SKILL.md) when you want the declarations, references or diagnostics of a file without reading it, or when you want to edit through an AST-aware rename. - **Memory and GC**: see [v-memory](../v-memory/SKILL.md) for GC modes, ownership checking and `unsafe`.
GitHub에서 보기