Describe the feature
error('msg') heap-allocates a MessageError on every call and boxes it into
the IError interface. This happens on the hot path of any !T ("result") function
that returns an error, even when the caller discards the result with or {} —
the allocation happens inside the callee, before it returns, regardless of what the
caller does with the value.
Current implementation, vlib/builtin/chan_option_result.v:128-134:
@[inline]
pub fn error(message string) IError {
trace_error(message)
return &MessageError{
msg: message
}
}The & heap-allocates a MessageError (GC-managed). The compiler lowers error(...)
to builtin___v_error(...). Generated C (v -printfn builtin___v_error file.v):
inline IError builtin___v_error(string message) {
;
GC_reachable_here(&message);
return I_MessageError_to_Interface_IError((HEAP(MessageError, ((MessageError){.msg = message,.code = 0,}))));
}HEAP(type, expr) expands to ((type*)builtin__memdup((void*)&((type[]){expr}[0]), sizeof(type)))
— a real allocation (builtin__memdup) plus interface boxing, per call.
This contrasts with none, which already has a zero-allocation cached sentinel
(vlib/builtin/chan_option_result.v:203-211):
const none__ = IError(&None__{})
struct None__ {
Error
}Use Case
A very common pattern is a "lookup / not-found" function on a hot path:
fn find(x int) !int {
if x < 0 {
return error('not found')
}
return x
}
fn main() {
mut n := 0
for i := -1_000_000; i < 0; i++ {
find(i) or { n++ } // result discarded, yet each error() above still allocated
}
println(n)
}Every miss allocates a MessageError on the heap, even though the error object is
immediately discarded. For tight loops, caches, parsers, and any "fast not-found path",
this is a per-call allocation that cannot currently be avoided while still using the
idiomatic !T / error() mechanism.
Generated main__find (identical under default and -prod):
VV_LOC _result_int main__find(int x) {
if (x < 0) {
return (_result_int){ .is_error=true, .err=builtin___v_error(_S("not found")), .data={E_STRUCT} };
}
...
}The callee always builds the boxed error (builtin___v_error → HEAP(...) →
builtin__memdup) before returning, so or {} at the call site cannot avoid it.
V already proves a zero-alloc error return is possible: ?T functions that
return none compile to a cached const, no allocation:
return (_option_int){ .state=2, .err=_const_none__, .data={E_STRUCT} };There is no equivalent for the very common "return an error with no extra state" case.
Proposed Solution
Provide a non-allocating, cached/sentinel error path for the common stateless-error
case, mirroring how none works. Possible approaches (any one would address this):
- A cached/static sentinel error for the stateless case (e.g. a public
error_noneconstant, or reusing a single staticIError), so a "not found" fast path can be expressed allocation-free. - A non-allocating
error()for compile-time-constant messages. String literals are already static, so theMessageErrorcould be emitted as a static instance instead ofHEAP(...)when the message is a constant. - A compiler optimization: when the result of
error(...)is provably discarded by the caller'sor {}(message unused), skip theHEAPallocation and return a cached sentinel.
The none machinery in vlib/builtin/chan_option_result.v:203-211 is the reference
implementation pattern.
Exact code involved: vlib/builtin/chan_option_result.v:129-134 (error), its lowering
to builtin___v_error, and the matching HEAP(MessageError, ...) expansion above.
Other Information
Reproduction verified with v run, v -printfn ..., and v -prod on the version below.
Swapping !int → ?int and return error('not found') → return none removes the
allocation (.err=_const_none__), demonstrating both the asymmetry and that a zero-alloc
path is feasible.
No existing duplicate was found via gh search issues --repo vlang/v for terms like
"error allocation", "MessageError", "zero allocation error", "cached error",
"IError boxing".
Acknowledgements
- I may be able to implement this feature request
- This feature might incur a breaking change
Version used
V 0.5.1 6dd9033de29819f9ada4011f783b1d8b9d47d7e8.eb1d47b
Environment details (OS name and version, etc.)
linux, Ubuntu 24.04 LTS; cc (GCC) 14.2.0
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.
Activity
0No activity yet.