{
"$type": "site.standard.document",
"path": "/Zig-Dot-Star-Syntax/",
"publishedAt": "2026-04-16T13:13:01.451Z",
"site": "https://www.openmymind.net",
"tags": [
"pointers",
"stack memory",
"@import"
],
"textContent": "Maybe I'm the only one, but it always takes my little brain a split second to understand what's happening whenever I see, or have to write, something like `value.* = .{...}`.\n\nIf we take a step back, a variable is just a convenient name for an address on the stack. When this function executes:\n\n\n fn isOver9000(power: i64) bool { return power > 9000; }\n\nSay, with a `power` of 593, we could visualize its stack as:\n\n\n power -> ------------- | 593 | -------------\n\nIf we changed our function to take a pointer to an integer:\n\n\n // i64 changed to *i64 fn isOver9000(power: *i64) bool { return power > 9000; }\n\nOur `power` argument would still be a label for a stack address, but instead of directly containing an number, the stack value would itself be an address. That's the _indirection_ of pointers:\n\n\n power -> ------------- | 1182145c0 |------------------------ ------------- | | ............. empty space | ............. or other data | | ------------- | | 593 | <---------------------- -------------\n\nBut this code doen't work: it's trying to compare a `comptime_int` (`9000`) with an `*i64`. We need to make another change to the function:\n\n\n // i64 changed to *i64 fn isOver9000(power: *i64) bool { // power changed to power.* return power.* > 9000; }\n\n`power.*` is how we dereference a pointer. Dereferencing means to get the value pointed to by a pointer. From our above visualization, you could say that the `.*` follows the arrow to get the value, `593`.\n\nThis same syntax works for writing as well. The following is valid:\n\n\n fn isOver9000(power: *i64) bool { power.* = 9001; return true; }\n\nLike before, the dereferencing operator (`.*`), \"follows\" the pointer, but now that it's on the receiving end of an assignment, we write the value into the pointed add memory.\n\nThis is all true for more complex types. Let's say we have a `User` struct with an `id` and a `name`:\n\n\n const User = struct { id: i32, name: []const u8, }; var user = User{ .id = 900, .name = \"Teg\" };\n\nThe `user` variable is a label for the location of the [start of] the user:\n\n\n user -> ------------- | 900 | ------------- | 3 | ------------- | 3c9414e99 | ----------------------- ------------- | | ............. empty space | ............. or other data | | ------------- | | T | <---------------------- ------------- | e | ------------- | g | -------------\n\nA slice in Zig, like our `[]const u8`, is a length (`3`) and a pointer to the values. Now, if we were to take the address of `user`, via `&user`, we introduce a level of indirection. For example, imagine this code:\n\n\n const std = @import(\"std\"); const User = struct { id: i32, name: []const u8, }; pub fn main() !void { var user = User{ .id = 900, .name = \"Teg\" }; updateUser(&user); std.debug.print(\"{d}\\n\", .{user.id}); } fn updateUser(user: *User) void { user.id += 100000; }\n\nThe `user` parameter of our `updateUser` function is pointing to the `user` on `main`'s stack:\n\n\n updateUser user -> ------------- | 83abcc30 |------------------------ ------------- | | ............. empty space | ............. or other data | | main | user -> ------------- | | 900 | <---------------------- ------------- | 3 | ------------- | 3c9414e99 | ----------------------- ------------- | | ............. empty space | ............. or other data | | ------------- | | T | <---------------------- ------------- | e | ------------- | g | -------------\n\nBecause we're referencing `main`'s `user` (rather than a copy), any changes we make will be reflected in `main`. But, we aren't limited to operating on fields of `user`, we can operate on its entire memory.\n\nOf course, we can create a copy of the id field (assignment are always copies, just an matter of knowing _what_ we're copying):\n\n\n fn updateUser(user: *User) void { const id = user.id // .... }\n\nAnd now the stack for our function looks like:\n\n\n user -> ------------- | 83abcc30 | id -> ------------- | 900 | -------------\n\nBut we can also copy the entire user:\n\n\n fn updateUser(user: *User) void { const copy = user.*; // .... }\n\nWhch gives us something like:\n\n\n updateUser user -> ------------- | 83abcc30 |--------------------- copy -> ------------- | | 900 | | ------------- | | 3 | | ------------- | | 3c9414e99 | --------------------|-- ------------- | | | | ............. empty space | | ............. or other data | | | | main | | user -> ------------- | | | 900 | <------------------- | ------------- | | 3 | | ------------- | | 3c9414e99 | -----------------------| ------------- | | ............. empty space | ............. or other data | | ------------- | | T | <---------------------- ------------- | e | ------------- | g | -------------\n\nNotice that it didn't create a copy of the value 'Teg'. You could call this copying \"shallow\": it copied the `900`, the `3` (name length) and the `3c9414e99` (address of the name pointer).\n\nJust like our simpler example above, we can also assign using the dereferencing operator:\n\n\n fn updateUser(user: *User) void { // using type inference // could be more explicit and do // user.* = User{....} user.* = .{ .id = 5, .name = \"Paul\", }; }\n\nThis doesn't copy anything; it writes into the address that we were given, the address of the main's `user`:\n\n\n updateUser user -> ------------- | 83abcc30 |------------------------ ------------- | | ............. empty space | ............. or other data | | main | | user -> ------------- | | 5 | <---------------------- ------------- | 4 | ------------- | 9bf4a990 | ----------------------- ------------- | | ............. empty space | ............. or other data | | ------------- | | P | <---------------------- ------------- | a | ------------- | u | ------------- | l | -------------\n\nIf you're still not fully comfortable with this, and if you haven't done so already, you might be interested in the pointers and stack memory parts of my learning zig series.\n\nLeave a comment",
"title": "Zig's dot star syntax (value.*)",
"updatedAt": "2025-03-07T00:00:00.000Z"
}