You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
V version: V 0.5.1 99f141f74 (verified no relevant commits land between that and current master b6dfae560)
OS-agnostic
Context
docs.md §Closures (lines 3245–3266) documents that [mut x] makes the closure's copy mutable but does not capture x by reference, so writes inside the closure are invisible to the caller. This is by design and works for stateful counter patterns. It is, however, a silent footgun for the much more common pattern of "let a helper closure update some local I'm building up."
The footgun
Downstream code routinely writes:
mut cur := ?T(none)
flush := fn [mut cur] (val T) {
if mut t := cur { /* mutate t */; cur = t }
}
for v in items { flush(v) }
// cur is silently unchanged.
The closure compiles, runs, and the mutation lands on the closure's private copy. The outer cur is never updated. There is no warning; the surface syntax is identical to what would be a sound pattern in most languages with explicit by-reference syntax (Rust &mut, C++ &, Swift inout).
In CX (a V-native data interchange library) this exact pattern landed in three conformance-test runners and silently made every fixture mismatch pass for one release cycle. The discovery was an accident.
Possible fixes
Listed in order of preference:
Reference-capture syntax — e.g. [&mut x], [ref x], or something else V-shaped. Makes the by-reference intent explicit at the capture site; today there is no way to express it.
Compile-time warning — when a captured [mut x] is written to inside the closure but never re-read outside the closure's own call sites, emit "captured copy of x is modified but the change does not propagate; pass x as a mut parameter to a regular function instead." Closes the footgun without changing semantics.
Documentation-only fix — surface the gotcha more prominently in docs.md §Closures and link it from the troubleshooting page. The current paragraph is technically accurate but easy to skim past.
Workaround used downstream
Rewrite the closure as a regular fn (...mut cur ...) function and call it explicitly. Works fine but breaks the closure-shaped reading of the call site.
Note
You can use the 👍 reaction to increase the issue's priority for developers.
Please note that only the 👍 reaction to the issue itself counts as a vote.
Other reactions and those to comments will not be taken into account.
Environment
V 0.5.1 99f141f74(verified no relevant commits land between that and current masterb6dfae560)Context
docs.md§Closures (lines 3245–3266) documents that[mut x]makes the closure's copy mutable but does not capturexby reference, so writes inside the closure are invisible to the caller. This is by design and works for stateful counter patterns. It is, however, a silent footgun for the much more common pattern of "let a helper closure update some local I'm building up."The footgun
Downstream code routinely writes:
The closure compiles, runs, and the mutation lands on the closure's private copy. The outer
curis never updated. There is no warning; the surface syntax is identical to what would be a sound pattern in most languages with explicit by-reference syntax (Rust&mut, C++&, Swiftinout).In CX (a V-native data interchange library) this exact pattern landed in three conformance-test runners and silently made every fixture mismatch pass for one release cycle. The discovery was an accident.
Possible fixes
Listed in order of preference:
[&mut x],[ref x], or something else V-shaped. Makes the by-reference intent explicit at the capture site; today there is no way to express it.[mut x]is written to inside the closure but never re-read outside the closure's own call sites, emit "captured copy ofxis modified but the change does not propagate; passxas amutparameter to a regular function instead." Closes the footgun without changing semantics.docs.md§Closures and link it from the troubleshooting page. The current paragraph is technically accurate but easy to skim past.Workaround used downstream
Rewrite the closure as a regular
fn (...mut cur ...)function and call it explicitly. Works fine but breaks the closure-shaped reading of the call site.Note
You can use the 👍 reaction to increase the issue's priority for developers.
Please note that only the 👍 reaction to the issue itself counts as a vote.
Other reactions and those to comments will not be taken into account.