Skip to content

Variables

Chung Leong edited this page Apr 24, 2024 · 3 revisions

Variables declared in a Zig module are accessible in JavaScript provided they're public. Unlike constants, variables cannot be imported individually. You must access them through the module, represented by the default export:

const std = @import("std");

pub const numeric_constant: i32 = 1234;
pub var numeric_variable: i32 = 43;

pub fn printNumericVariable() {
    std.debug.print("From Zig: {d}\n", .{numeric_variable});
}
import module, { numeric_constant, printNumericVariable } from './variable-example-1.zig';

console.log(numeric_constant);
console.log(module.numeric_variable);
module.numeric_variable = 777;
printNumericVariable();
1234
43
From Zig: 777

Limitations

Zig variables are stored in a separate memory space. On platforms that do not permit the creation of external buffers (namely Electron), variables are not accessible.

A pointer that exist in this memory space can only point to objects living in the same space. It cannot point to an object in JavaScript memory, whose address can change over time. An error would occur if you attempt to assign such an object to a pointer variable in a module:

const std = @import("std");

pub const User = struct {
    name: []const u8,
    role: []const u8,
};

pub const default_user: User = .{
    .name = "nobody",
    .role = "none",
};
pub var current_user: *const User = &default_user;

pub fn printCurrentUser() void {
    std.debug.print("{s} ({s})\n", .{ current_user.name, current_user.role });
}
import module, { printCurrentUser, User } from './variable-example-2.zig';

printCurrentUser();
try {
    module.current_user = new User({ name: 'batman72', role: 'vigilante' });
} catch (err) {
    console.log(err.message);
}
nobody (none)
Pointers in fixed memory cannot point to garbage-collected object

In order to create an object that exists in Zig's memory space, you'd need to use the constructor's fixed option:

import module, { printCurrentUser, User } from './variable-example-2.zig';

printCurrentUser();
module.current_user = new User({ name: 'batman72', role: 'vigilante' }, { fixed: true });
printCurrentUser();
nobody (none)
batman72 (vigilante)

When the object is created through auto-vivification, Zigar will automatically select the right memory:

import module, { printCurrentUser } from './variable-example-2.zig';

printCurrentUser();
module.current_user = { name: 'joker1999', role: 'clown' };
printCurrentUser();
nobody (none)
joker1999 (clown)

Zigar does not automatically free memory allocated in this manner. If the pointer can be overwritten later, you would have to manually free the current object:

import module, { printCurrentUser } from './variable-example-2.zig';

printCurrentUser();
module.current_user.name.delete();
module.current_user.delete();
module.current_user = { name: 'joker1999', role: 'clown' };
printCurrentUser();