These rules apply to all Rust-to-V translated code in this repository.
-
Translate the logic in the current file completely. Only references into yet-untranslated files may be stubbed. Do not replace logic in the file being translated with stubs.
-
Keep the translated structure close to the Rust source. Preserve names, control flow, helper boundaries, and API shape unless V or ownership semantics force a different representation.
-
Use V ownership mode as the target model. If faithful translation needs an ownership feature that V does not yet support, implement the missing feature in the local V compiler instead of weakening the translated code.
-
If a compiler bug is encountered while translating, fix the compiler bug instead of routing around it in the translated code. Add the focused compiler tests needed for the fix before relying on the new behavior.
-
Keep V's standard
flagmodule ownership-free. If translated ownership-aware code needs flag-style parsing, put that work in a separate module instead of makingvlib/flagan ownership dependency.
-
If translated code calls into an untranslated module, create the minimal module/file needed so the code builds.
-
Stubs must be clearly limited to untranslated dependencies. Translated files themselves must contain real logic.
-
Translate original Rust comments and doc comments directly. Do not paraphrase them, rewrite them in a different tone, or replace them with autogenerated summaries.
-
Preserve the original documentation structure where practical. If Rust had a title line plus a follow-up paragraph, keep that shape in V comments.
-
Only auto-generate comments when the Rust source does not document the V-exposed surface directly. Typical cases:
- V-only public fields created from private Rust fields
- Port-only helper fields introduced because V cannot represent the Rust shape directly
- V-specific helper functions or representation notes
- Explicit deviations from the Rust implementation
- When a comment comes from the Rust source, prefer the source wording. Only adjust wording when the port changed behavior or representation and the original text would become misleading.
-
Translate Rust
#[cfg(test)]modules into separate V_test.vfiles. Do not leave translated tests inline in the main translated source file. -
Keep translated test helpers and test comments close to the Rust source. Apply the same comment-translation rules to tests.
-
When a shared translated test helper must be reused across multiple
_test.vfiles, keep it in a normal module file only if the current ownership frontend cannot load sibling_test.vhelpers during directv -ownership some_test.vcompilation. Document that frontend limitation in the helper file comment when this exception is used.
-
Use
.to_owned()when creating a new owned string from borrowed/derived data. Examples: constructor inputs, normalized paths, joined paths, trimmed strings, literals promoted into owned storage. -
Use clone semantics for copying existing owned values. For builtins this means normal V
.clone()/generated helper behavior. -
Rust
#[derive(Clone)]maps toimplements IClone. Prefer the compiler-generated clone for these types. -
A handwritten Rust
impl Clonemaps to an explicit Vclone()with the same logic. -
Only keep a handwritten
clone()when the type needs custom behavior that the autogeneratedIClonepath does not express cleanly. Example: custom tagged-union/internal-representation handling.
-
Translate all Rust lifetimes in translated code. Do not drop, silently erase, or leave lifetime-bearing Rust APIs unparameterized in V unless the original code is actually owning/cloning the data or a documented V/compiler limitation forces a deviation.
-
When the Rust API uses lifetimes to express borrowing relationships, translate them with V explicit lifetimes instead of erasing them. Use
^alifetime parameters,&^a Treferences,Type[^a]lifetime-parameterized types, andfn foo[^a](...)orpub fn (x &^a T) bar[^a](...)for functions and methods. -
Do not replace borrowed Rust return values with owned V values just to avoid lifetimes. If the Rust return type borrows from
self, an argument, or nested matcher state, the translated V type should carry the same lifetime relationship. -
If a translated type exists only to carry borrowed data, keep it lifetime-parameterized even when the lifetime has no runtime representation. Do not collapse lifetime-only generic parameters out of the translated API shape.
-
Only omit or rewrite Rust lifetimes when the original code is actually cloning/owning the data or a documented V/compiler limitation forces a deviation. When a deviation is unavoidable, document it in the translated code comments.
-
Rust
Option<T>should be translated directly as V?Twhen V supports the type in that position. -
Only use an explicit fallback representation like
Tplushas_* boolwhen V cannot represent the original Rust shape directly or a documented port-specific constraint requires it. Document the deviation when this happens. -
This also applies to translated struct fields: prefer a direct
?Tfield over splitting it intovalueplushas_value boolwhen V supports the optional field shape. -
If a Rust-private field must become public in V for practical reasons, keep the representation explicit and document only the V-specific parts.
- After changing translated ownership-aware code, verify it builds with V ownership enabled.
For the
ignoremodule, use:
cd ignore
v -shared -ownership .-
After changing the local V compiler for translation support, run focused V2 tests for the affected checker/transformer behavior before relying on the new feature in translated code.
-
When translated tests exist, verify them from the translated
_test.vfiles. Ifv -ownership test ...is not supported by the current frontend path, compile and run the_test.vfile directly withv -ownership.
-
Do not leave temporary smoke-test binaries or generated
.cfiles in the repository. -
Keep translation-policy changes in this file so future translations follow the same rules.