VaLists in Rust - pt. 1

Nov 13, 2018

Overview

After rust-lang/rust#49878 (The first PR implementing RFC 2137) some support for va_lists has been merged into base rust. Variadic arguments are used by C functions when a variable number of arguments need to be passed to an argument, e.g. printf().

As an example, we can create a function add_n() that will add up to n integers passed to the function. A simple implementation of this function in C might look something like the following:

#include <stdarg.h>

int add_n(int n, ...) {
  va_list ap;
  int i, sum = 0;
  va_start(ap, n);
  for (i = 0; i < n; ++i) {
    sum += va_arg(ap, int);
  }
  va_end(ap);
  return sum;
}

The important pieces of this function are the following:

  • The va_start macro initializes the va_list
  • The va_arg macro fetches the next value of the given type from the va_list.
  • The va_end macro is used after we’re done with the list.

We can now use add_n() with something like the following:

  assert(add_n(3, 1, 2, 3) == 6);
  assert(add_n(2, 20, 22) == 42);

vprintf()-like functions

Most procedures that use va_lists are actually broken across two functions. For example, printf() is accompanied by vprintf(). Let’s do the same with our add_n() function, by adding a vadd_n() function.

#include <stdarg.h>

int vadd_n(int n, va_list ap) {
  int i, sum = 0;
  for (i = 0; i < n; ++i) {
    sum += va_arg(ap, int);
  }
  return sum;
}

int add_n(int n, ...) {
  va_list ap;
  int sum;
  va_start(ap, n);
  sum = vadd_n(n, ap);
  va_end(ap);
  return sum;
}

Note that all of the initialization and management of the va_list occurs in add_n(), while the “real” work occurs in vadd_n().

Writing vprintf()-like functions in rust

We can implement vadd_n() in rust with something like the following:

#![feature(c_variadic)]

use libc::c_int;
use std::ffi::VaList;

#[no_mangle]
pub unsafe fn vadd_n(argc: c_int, ap: VaList) -> c_int {
    let mut sum = 0;
    for i in 0..argc {
        sum += ap.arg::<c_int>();
    }
    sum
}

VaList is experimental and only available in nightly rust with the c_variadic feature enabled.

rust-lang/rust#49878 is the first of many PRs to add support for va_lists in rust. It only allows for implementing vprintf()-like functions. Support for implementing functions that require initializing the va_list will be added in a follow-up PR, so for now we’ll have to implement add_n() in C with something like the following:

#include <stdarg.h>

extern int vadd_n(int, va_list);

int add_n(int argc, ...) {
  va_list ap;
  int sum;
  va_start(ap, argc);
  sum = vadd_n(int, va_list);
  va_end(ap);
  return sum;
}

Huzzah!