A colleague of mine was trying to use Rust to write a debugging library that intercepted specific C function calls and interposed tracing. Some of those C function calls used variadic arguments (varargs). Rust supports declaring external variadic functions with extern "C"
, but does not support declaring such functions with a body. While it might be possible to dissect a va_list
once obtained, it isn’t currently possible to write the variadic function itself in Rust, and obtain a va_list
from the ...
. And even once you have the va_list
, you’d have to write your own custom assembly routines to extract the arguments, even though LLVM already has built-in intrinsics for that on every target platform (e.g. llvm.va_start
and llvm.va_end
).
I think it makes sense to have built-in compiler support for this, based on the corresponding LLVM intrinsics.
I’d like to propose a minimal RFC, that allows declaring an extern "C"
function in Rust (with a body), and calling (unsafe) intrinsics from within such a function, to obtain and process a native Rust type VaList
that corresponds to va_list
.
That could look something like this:
#[no_mangle]
pub unsafe extern "C" fn func(fixed_arg: SomeType, ...) {
let args = std::intrinsics::va_start();
let arg1 = args::next::<i32>();
let arg2: *const c_char = args::next();
}
The VaList
type would provide a single function, next
, which can return any type that implements a specific marker trait; the types supported by LLVM’s intrinsic should implement that marker trait. The type would implement Drop
(to call the equivalent of va_end
), and would not implement ?Sized
or Copy
or Sync
or Send
. It could potentially implement Clone
(based on the intrinsic for va_copy
).
Note that the caller could pass the VaList
down the call stack, but could not allow it to propagate up past the variadic function itself. Since all the intrinsics are unsafe anyway, that doesn’t seem excessively unreasonable, though it might be nice to enforce that somehow if we have a straightforward means of doing so.
(I could imagine alternative syntactic sugars that desugar to the above, such as naming the ...
in the argument list rather than calling an intrinsic, but that seems excessively magic.)
Does this seem reasonable?