Variadic Functions
Variadic functions accept a variable number of arguments. Vex supports C-compatible variadics for FFI and provides builtin variadic support for formatting macros like $println.
C-Compatible Variadic Functions
For interop with C libraries like printf, declare the function with ... as the final parameter:
extern "C" {
fn printf(format: *u8, ...): i32
fn scanf(format: *u8, ...): i32
fn snprintf(buf: *u8!, size: usize, format: *u8, ...): i32
}Calling C Variadics
unsafe {
printf("Hello %s, you are %d years old\n", name, age)
printf("Values: %d, %f, %s\n", 42, 3.14, "test")
}Important: When calling C variadic functions:
- Type checking of variadic arguments is minimal -- ensure argument types match the format string.
- Default argument promotions apply (e.g.,
f32promotes tof64in variadic position). - Use
unsafeblocks -- variadic calls bypass type safety.
Builtin Variadic Macros
Vex provides variadic builtin macros that are type-safe and do NOT require unsafe:
$println
$println("Hello") // single argument
$println(f"Value: {x}") // template literal
$println(f"{} + {} = {}", a, b, a + b) // multiple format args$eprintln (stderr)
$eprintln(f"Error: {msg}")$panic
$panic(f"Assertion failed: expected {expected}, got {actual}")$format (string formatting, variadic)
let msg = $format(f"Processing {} of {} items", current, total)Defining Variadic Vex Functions
Vex does NOT currently support defining new variadic functions with ... syntax for pure Vex code. Variadic definitions are restricted to:
extern "C"declarations for FFI- Builtin compiler macros (like
$println)
For variable-argument needs in pure Vex, use these alternatives:
Use Vec<T> for homogeneous variadic args
fn sumAll(values: Vec<i32>): i32 {
let! total = 0
for v in values {
total += v
}
return total
}
let result = sumAll(Vec.from([1, 2, 3, 4, 5]))Use default parameters where applicable
fn createWindow(title: string, width: i32 = 800, height: i32 = 600): Window {
// ...
}
let w1 = createWindow("Main") // defaults
let w2 = createWindow("Settings", height: 400) // named overrideUse builder pattern for complex optional args
struct Config {
host: string,
port: i32,
timeout: i32,
retries: i32,
}
fn Config.new(host: string, port: i32): Config { ... }
fn (self: &Config!) set_timeout!(timeout: i32) { self.timeout = timeout }
fn (self: &Config!) set_retries!(retries: i32) { self.retries = retries }
// Builder-style usage
let! config = Config.new("localhost", 8080)
config.set_timeout!(5000)
config.set_retries!(3)Variadic Argument Access (Compiler Internals)
For compiler developers: The Vex compiler lowers variadic calls through calls/variadic.rs. Each target platform has its own calling convention for variadics (System V AMD64 ABI on Linux/macOS, different on Windows and ARM).
Safety Considerations
- C variadics are unsafe: No type checking on variadic arguments. Mismatched types cause undefined behavior.
- Prefer builtin macros:
$println,$formatetc. are type-safe alternatives. - Don't mix Vex types with C variadics: Pass only C-compatible types (primitives, pointers) to C variadic functions.
- String handling: Vex
stringis NOT C-compatible. Cast to*u8for C variadics:
let msg = "Hello"
unsafe {
printf("%s\n", msg as *u8) // pass as C string pointer
}