RZig lets an R package implement functions in Zig. The package author writes ordinary Zig functions and marks the exported ones; RZig generates the registration, the R wrappers and the conversion of every argument and result, and runs the Zig build when the package is installed. This vignette goes through the workflow one step at a time. Every step up to the installation is plain R and runs without Zig; the steps that compile run when a supported Zig compiler is found, and this copy of the vignette ran them.
find_zig() looks for a Zig release of the supported
series (0.16 and 0.17) in ZIG, on PATH, in the
folder of install_zig() and in the usual install locations.
With required = FALSE, a missing compiler is a result and
not an error:
compiler
#> Zig compiler: /opt/homebrew/bin/zig (version 0.16.0, supported)
#> rzig supports Zig 0.16.x or 0.17.x.A user without Zig can install a verified release with
install_zig(), which downloads it into the user cache
folder of rzig after asking for permission.
create_rzig_package() creates a new package with the
RZig scaffold, a starter function and a test; use_rzig()
adds the same scaffold to an existing package. The package here lives in
R’s temporary directory:
path <- file.path(tempdir(), "hellozig")
create_rzig_package(path, fields = list(
Title = "Greetings from Zig",
Description = "Shows the workflow of 'rzig'."
))
#> Created package hellozig at /private/var/folders/vh/njwnyy0141n0zbrgjw2vbz800000gn/T/RtmppmxRLi/hellozig
#> Write Zig functions in src/rzig/src/main.zig, then run rzig::document(path) and rzig::build_zig(path)The Zig code lives in src/rzig/src/main.zig. The starter
function shows the three parts of an export: documentation comments,
which become the help page, the marker /// @export, and a
public function with supported types:
main_zig <- file.path(path, "src", "rzig", "src", "main.zig")
cat(readLines(main_zig), sep = "\n")
const std = @import("std");
const builtin = @import("builtin");
const rzig = @import("rzig");
/// Route ReleaseSafe failures through R while retaining Zig test behavior.
pub const panic = if (builtin.is_test)
std.debug.FullPanic(std.debug.defaultPanic)
else
rzig.Panic;
/// Return a friendly greeting.
/// @param name A name to greet.
/// @return A character string.
/// @examples
/// hello_zig("R")
/// @export
pub fn hello_zig(ctx: *rzig.Ctx, name: []const u8) rzig.Error![]const u8 {
return std.fmt.allocPrint(ctx.allocator(), "Hello, {s}!", .{name}) catch
rzig.raise("out of memory while building the greeting", .{});
}
comptime {
rzig.registerModule(@This());
}A second export takes a numeric vector, which Zig reads as a borrowed
slice, and returns a numeric vector that Zig allocates in the call
context ctx:
cat(
"",
"/// The cumulative sum of a numeric vector.",
"/// @param x A numeric vector.",
"/// @return A numeric vector of the same length.",
"/// @examples",
"/// cumulative(c(1, 2, 3))",
"/// @export",
"pub fn cumulative(ctx: *rzig.Ctx, x: []const f64) rzig.Error![]f64 {",
" const out = try ctx.alloc(f64, x.len);",
" var total: f64 = 0;",
" for (x, out) |value, *slot| {",
" total += value;",
" slot.* = total;",
" }",
" return out;",
"}",
sep = "\n", file = main_zig, append = TRUE
)scan_exports() lists the exported functions with their R
arguments and the R values they accept and return:
scan_exports(path)
#> Zig exports of package hellozig (src/rzig/src/main.zig): 2 functions
#>
#> hello_zig(name)
#> name []const u8 character, length 1, copied
#> returns rzig.Error![]const u8 character, length 1
#>
#> cumulative(x)
#> x []const f64 double vector, borrowed
#> returns rzig.Error![]f64 double vectordocument() writes the generated files: the Zig manifest,
the R wrappers, the NAMESPACE lines and the help pages. Run
it after every change of an exported signature or its documentation:
bindings <- document(path)
#> Generated RZig bindings for hellozig: 2 exported functions
cat(bindings$wrappers)
#> # Generated by rzig::document(). Do not edit.
#>
#> #' Return a friendly greeting.
#> #' @param name A name to greet.
#> #' @return A character string.
#> #' @examples
#> #' hello_zig("R")
#> #'
#> #' @export
#> hello_zig <- function(name) {
#> .Call(hello_zig_, name)
#> }
#>
#> #' The cumulative sum of a numeric vector.
#> #' @param x A numeric vector.
#> #' @return A numeric vector of the same length.
#> #' @examples
#> #' cumulative(c(1, 2, 3))
#> #'
#> #' @export
#> cumulative <- function(x) {
#> .Call(cumulative_, x)
#> }rzig_status() runs the checks that
build_zig() runs before it compiles: the
DESCRIPTION, the scaffold, the Zig source, the types of the
exports, the generated files and the toolchain:
rzig_status(path)
#> RZig status of package hellozig
#> [ok] DESCRIPTION package hellozig
#> [ok] Scaffold build files and framework sources present
#> [ok] Zig source src/rzig/src/main.zig
#> [ok] Exports 2 functions: hello_zig, cumulative
#> [ok] Bindings generated files match the exports
#> [ok] Zig compiler /opt/homebrew/bin/zig (version 0.16.0)
#> [ok] C toolchain C compiler clangbuild_zig() installs the package from a copy, so that
the source folder stays clean, and reports compiler errors by file and
line:
build <- build_zig(path)
build
#> Build of package hellozig: succeeded in 8.8 seconds
#> Zig compiler: /opt/homebrew/bin/zig (version 0.16.0)
#> Installed into a library in the session temporary directory
library(build$package, character.only = TRUE, lib.loc = build$library)
hello_zig("R")
#> [1] "Hello, R!"
cumulative(c(1, 2, 3, 4))
#> [1] 1 3 6 10Arguments of the wrong type are R errors that name the argument; the R session continues:
Functions that do not use the rzig module can be tested
with Zig’s own test runner, without R. test_zig() runs the
test blocks reached from
src/rzig/src/tests.zig:
zig_function() compiles one Zig function into a
temporary package and returns an R function, which suits experiments
before code moves into a package:
The README of rzig lists the supported types. The other vignettes describe updating the scaffold, exports and classes, threads and random numbers, numerical routines from R, packages that share code, and distribution.