V — Reference
Source: https://docs.vlang.io/
V
- Created: 2019 by Alexander Medvednikov; first public release June 2019
- Latest stable: V 0.5.1 (2026-03-09); previous: V 0.5.0 (2025-12-31)
- Status: Pre-1.0; breaking changes still occur. Treat as “actively evolving” — pin a commit/version for production
- Paradigms: Multi-paradigm (imperative + procedural + light functional + structs/methods)
- Typing: Static, strongly typed, type inference, generics, sum types, interfaces (structural-ish)
- Memory: Minimal tracing GC by default; opt-in
-autofree(compiler-insertedfree, ~90-100% coverage, WIP);-gc nonefor manual;-preallocarena - Compilation: Source → C → native via gcc/clang/tcc/msvc; also direct AMD64/ARM64/WebAssembly backends; very fast (~400k LoC/sec on x64); self-hosting compiler bootstraps in <1s
- Primary domains: CLI tools, game/graphics, web (Veb), GUI (UI), embedded, scientific
- Official docs: https://docs.vlang.io/ · Source: https://github.com/vlang/v
At a glance
V’s pitch is “Go-like simplicity, C-like speed, Rust-like safety, in one tiny binary.” Designed by Alexander Medvednikov; community-driven via the V Foundation. Reality: pre-1.0 with a long history of contested marketing claims (autofree completeness, “memory safety by default,” compile speed). The compiler is genuinely fast and produces small binaries, the standard library is broad (web, ORM, GUI, crypto), and v ships everything in one ~1MB binary. New backends in 0.5.x (cleanc, ssa/amd64, ssa/arm64) are converging on a self-hosting non-C path.
Getting started
Install (no separate version manager — V is self-updating):
git clone --depth=1 https://github.com/vlang/v
cd v && make # Linux/macOS
.\make.bat # Windows (MSVC or gcc)
sudo ./v symlink # put `v` on PATH
v up # update to latest
v versionHello world (hello.v):
fn main() {
println('Hello, V!')
}Run: v run hello.v. Compile: v hello.v (produces hello binary).
Project layout (created by v new myapp or v init):
myapp/
├── v.mod # module manifest
├── src/main.v # entry point
└── tests/ # *_test.v files run by `v test .`
Build/package tool: v itself. v install <module> (VPM), v install --git https://..., v build-module ., v doc ., v fmt -w ., v vet ., v test .. The v.mod file declares deps. Hex-style central registry at https://vpm.vlang.io/.
REPL: v repl (line-oriented; limited — feeds into a main() and recompiles).
Basics
Types & literals:
- Numeric:
i8 i16 int(=i32) i64 isizeandu8 u16 u32 u64 usize,f32 f64,rune(alias fori32),bool,string(immutable, UTF-8) 0b10,0o17,0x2Aliteral forms;_separator (1_000_000)- Strings: single-quoted
'hi'is the default; backtick\a`isrune;r’\n’raw;’x = {expr}consistently — old$var` form is deprecated)
Variables & scoping: Immutable by default with :=, mutable via mut:
x := 10 // immutable
mut y := 20 // mutable; reassign with =
y = 30
const pi = 3.14 // module-level onlyBlock-scoped. No shadowing across scopes by default. Unused vars/imports are errors, not warnings.
Control flow: if/else/else if (also expression-form: x := if cond { 1 } else { 2 }). for is the only loop:
for i in 0 .. 10 { ... } // exclusive range
for i, v in arr { ... }
for cond { ... } // while
for { ... } // infinitematch for sum types / values, with exhaustiveness:
match x {
1 { println('one') }
2, 3 { println('two or three') }
else { println('other') }
}Functions: pub fn for exports. Multiple return values. Named results with ?/! for fallible:
pub fn divide(a int, b int) !int {
if b == 0 { return error('div by zero') }
return a / b
}Strings: UTF-8, immutable. s.len is bytes, not runes; iterate runes via s.runes(). Slicing returns a substring view: s[1..4]. Concatenation with +. f-strings via '${expr:format}'.
Collections: Arrays [1, 2, 3] (typed []int), maps {'a': 1, 'b': 2} (map[string]int), and built-in []&Foo for slice-of-pointers. Array methods: .map(), .filter(), .any(), .all(), .sort() (mutating), .sorted() (non-mutating).
Intermediate
Type system depth:
- Structs with field defaults, embedded structs (composition),
pub mut/mut/pub/private visibility per field - Interfaces are structural — implementations are implicit;
interface Stringer { str() string } - Sum types:
type Token = Number | Word | Punctthen match-as - Generics:
fn map[T, U](s []T, f fn(T) U) []Uwith explicit instantiation when inference fails (map[int, string](xs, ...)) - Option
?Tand Result!Tare distinct:?= may be none,!= may error. Propagate with?/!postfix; default withor { ... }
Modules: Directory = module. module foo declaration. Imports via import vweb, import json, import x.json2. No relative imports.
Error handling: No exceptions. Functions returning !T (error-or-value) or ?T (option). Caller must handle with or { ... }, propagate with trailing !/?, or call .unwrap() (panic on error). panic('msg') for unrecoverable.
Concurrency: Two primitives:
spawn fn_call()→ OS thread (thread Thandle);.wait()to joingo fn_call()→ V coroutine (lighter, where the async runtime is enabled)
Channels are Go-style: ch := chan int{cap: 10}, ch <- 5, v := <-ch, ch.close(). select { ... } for multi-channel. Shared mutable state requires shared keyword + lock x { ... } / rlock x { ... } blocks; only structs/arrays/maps may be shared.
I/O: os (files, env, args, exec), net, net.http, net.websocket, io, time, json, json2, crypto.*, regex (now with non-greedy quantifiers in 0.5.1), db.sqlite/db.pg/db.mysql, sync, runtime.
Stdlib highlights: Built-in ORM (compile-time SQL generation), built-in Veb web framework, gg/ui for graphics/GUI, vweb.assets, pico minimal HTTP, flag arg parser, cli framework, term terminal styling, toml/yaml/xml/csv parsers.
Advanced
Memory model: Default GC is a small mark-sweep collector (Boehm-style). [heap] attribute on structs forces heap allocation for refs that escape. [noinit] skips zero-init. Autofree is the controversial flagship — compiler analyzes lifetimes and inserts free() at scope exit; it claims 90-100% coverage with GC catching the rest. As of 0.5.x autofree is still labeled WIP and is not the default. -prealloc arena allocates one big buffer freed at exit (great for compilers, bad for long-running services).
Concurrency deep dive: V coroutines run on a Photon-derived (libphoton) green-thread runtime when enabled with -coroutines. go uses these; spawn always uses OS threads. Channels implement Go’s CSP semantics; select supports timeouts (> 100 * time.millisecond { ... }). Thread-safety is enforced syntactically: you cannot pass a non-shared mutable to another goroutine without the compiler erroring.
FFI: First-class C interop:
#include <math.h>
fn C.cos(f64) f64
fn main() { println(C.cos(0.5)) }#flag -lfoo for linker flags. unsafe { ... } block required for raw pointer arithmetic. v translate file.c attempts C → V translation (limited). Inline assembly via asm amd64 { ... } blocks.
Reflection: Compile-time only via $for field in T.fields { ... }. Runtime type info is minimal — V favors compile-time codegen. typeof(x).name, typeof(x).idx for runtime tags on sum types.
Performance tools: v -prof program.v injects a sampling profiler. v -cflags -pg for gprof. v -d trace_gc for GC events. v -showcc prints the underlying C invocation for debugging.
God mode
Comptime metaprogramming:
$if linux { ... } $else $if windows { ... }for cond compilation$for field in T.fields { println(field.name) }to walk struct fields at compile time$compile_error('unsupported')to abort compilation with message$tmpl('view.html')for compile-time template rendering used by Veb$env('HOME')reads env var at compile time$embed_file('asset.png')bakes a file into the binary
Source → C pipeline: Look at v -o out.c file.v to see the generated C. The 0.5+ cleanc backend produces dramatically more readable C and is now self-hosting (compiles V itself). The new ssa/amd64 and ssa/arm64 backends generate native code without going through C — compile times drop further but stdlib coverage lags the C path.
Hot reload: Mark a function [live] fn foo() and run with v -live run app.v. Recompiles + reloads on file change without restarting. Used heavily in V’s GUI/game projects.
v translate: Bundled converter that translates C (and historically attempted Go/JS) to V. Quality varies — useful for porting headers, not whole projects.
Cross-compilation out of the box: v -os windows hello.v, -os linux, -os macos, -arch arm64. No toolchain hunting — V bundles tcc as a fallback C compiler.
Inline C:
#include <stdio.h>
fn main() {
a := 5
C.printf(c'a = %d\n', a)
}The c'...' literal is a C string (&u8).
The “memory-safe-by-default” claim — what it actually means: V prevents some common C bugs (no manual malloc/free in safe code, bounds checks on arrays, no implicit nullability), but does NOT have Rust-style borrow checking. Aliased mutable references are allowed; data races are caught only across go/spawn boundaries via shared. Autofree’s coverage claims have been disputed since 2019. Treat V as “much safer than C, much less rigorous than Rust.”
Idioms & style
- Naming:
snake_casefor vars/fns/files,PascalCasefor types/enum variants,SCREAMING_SNAKEfor consts - Formatter:
v fmt -w .is mandatory — there is exactly one official style and CI rejects unformatted code - Linter:
v vet .,v missdoc .(missing docs),v check-md .(markdown code blocks) - One-style philosophy: Like Go — no debate, vfmt decides.
mutonly when needed. Errors handled at the call site, not at the throw site. Avoid unsafe blocks in app code - Expert review focus: Watch for misuse of
unsafe { }(raw pointer ops), missedor { }on?/!returns,[heap]annotations forced by aliasing patterns, misuse ofsharedoutside structs/arrays/maps, and assumptions about autofree that don’t hold without-autofree
Ecosystem
| Domain | Library |
|---|---|
| Web | Veb (built-in, was vweb), pico (minimal), valval |
| GUI | vlang/ui (cross-platform), gg (2D graphics, ImGui-style), iui |
| Game | vsdl2, raylib bindings, the Volt game engine |
| Database | Built-in ORM (sqlite/pg/mysql), vsql |
| HTTP client | net.http (stdlib), vesper |
| CLI | flag and cli (stdlib) |
| Test | Built-in _test.v files, v test . |
| Docs | Built-in v doc, hosted on https://modules.vlang.io/ |
Notable users: Volt (Discord-like client written in V), VEX (web framework), Verdek (orchestration), Coachonko (CMS). V’s own self-hosting toolchain (compiler + vfmt + vls) is by far the largest V codebase.
Gotchas
- Pre-1.0: Breaking changes between minor releases. The 0.5 series introduced the
${var}interpolation form; old$varis removed - Autofree is opt-in and WIP — default is GC. Don’t believe blog posts claiming “no GC by default”
- Compile-speed claims are best-case — measured against the C-backend with tcc; ssa backends are still maturing; first compile of stdlib is much slower than incremental
spawnuses OS threads, not goroutines — high-concurrency code wantsgo+-coroutiness.lenis byte length, not rune count — uses.runes().lenfor char count- Unused imports/vars are hard errors —
// vfmt offwon’t help; remove them - Sum-type matching must be exhaustive — and
matchrequireselsefor non-sum-type values - Generics inference is best-effort — pre-0.5.1 cases sometimes need explicit
[T]instantiation; the new-new-generic-solverimproves this - C backend leaks identifiers — debugging stack traces show V-mangled C names. Use
-cstrictto catch warnings as errors - The
&(reference) and*(deref) operators look like C but allocate via[heap]rules — references that escape force heap allocation v translatefor non-C inputs is largely abandoned — Go/JS translation works on toy programs only
Citations
- V official site: https://vlang.io/
- V documentation: https://docs.vlang.io/
- V GitHub releases: https://github.com/vlang/v/releases
- V 0.5.1 release notes (2026-03-09): https://github.com/vlang/v/releases/tag/0.5.1
- V 0.5.0 release notes (2025-12-31): https://github.com/vlang/v/releases/tag/0.5.0
- Concurrency: https://docs.vlang.io/concurrency.html
- Memory management: https://docs.vlang.io/memory-management.html
- VPM (package registry): https://vpm.vlang.io/
- Module docs: https://modules.vlang.io/