by our incantations we manipulate the dance of electrons in the microcosm, the very fabric of the universe itself
.?
is equivalent to orelse unreachable
.
So to unwrap you can either do const y = x orelse <default>;
or if (x) |unwrapped_x| {}
;
One way to debug ZLS lookups is to do
zig run zls/zig-out/bin/build-runner.zig --pkg-begin "@build@" path/to/your/build.zig --pkg-end
and make sure it outputs a list of packages.
Using slices for a stringmap https://zig.news/andrewrk/how-to-use-hash-map-contexts-to-save-memory-when-doing-a-string-table-3l33
Allocgate https://pithlessly.github.io/allocgate.html
const vertex_s = packed struct {
x: [2]f32 = .{ 0.0, 0.0 },
y: [2]f32 = .{ 0.0, 0.0 },
z: [2]f32 = .{ 0.0, 0.0 },
};
pub fn main() !void {
const v = vertex_s{
.x = .{ 2.0, 1.0 },
.y = .{ 3.0, 5.0 },
.z = .{ 6.0, 10.10 },
};
Z_STD.debug.print("{}\n", .{v});
}
------- cribbed from @ssteinbach's gists:
From conversation w/ @spex_guy, who kindly explained the details below.
- A type defines a range of memory, and may contain pointers to other ranges.
- All copies of any type are shallow, copying only the immediate range of the type, and not anything it points to.
*T
is a value of only eight bytes, so copying it does not copy the memory it points to.[N]T
: contiguous buffer of typeT
and compile time known lengthN
.[N]T
is equivalent toextern struct { v0: T, v1: T, ..., vN: T }
, so it will copy all values by value.[_]T
: same as[N]T
but theN
is inferred by looking at the right side of the expression:var foo: [_]i32 = .{ 1, 2, 3}
is the same asvar foo: [3]i32 = .{ 1, 2, 3 }
- if you return a value of type
[N]T
, that is a value type and will be returned by value
[]T
: a "slice" whose length is not known at compile time. has a.len
and.ptr
.[]T
is equivalent tostruct { len: usize, ptr: [*]T }
- ...therefore a return type of []T will copy the
len
and pointer but not the data it points to.
std.ArrayList(T)
is a[]T
but with extra machinery to add more stuff on the end, allocating when necessary- its
.items
is a[]T
ArrayList.{append/appendSlice/etc}
will copy data during the append
- its
?T
is equivalent tostruct { valid: bool, payload: T }
, so it will copy the payload by value.ErrSet!T
is equivalent tostruct { error: ?ErrSet, payload: T }
, so it will copy the payload by value.allocator.dupe(...)
can be used to copy the contents of something from the stack into an allocated (IE longer memory life) chunk of memory
- Parameters may be passed by value or reference. Code must be written so that either approach is valid.
- When writing function arguments, if the address is actually important, use
*const T
, otherwise pass by value. - Struct return values will elide a copy between the x = foo() at the call site and the return .{} expression inside the function.
- For something like your failing test, any time you use & you are creating a graph cut that won't be copied across. Whenever you use it you should consider the lifetime of the thing you are addressing. Temporaries and locals live only until the end of the containing scope, so if you need it to last longer you need to tie it to an object with a longer lifetime. This means either heap allocating, or having an object with longer life passed in by the user.
- Slicing will also create a graph cut if you are slicing an array. Slicing a slice gives you a peer, where both the new slice and the source slice are separate from each other and from the underlying data.
- you can use = undefined if you are knowingly not initializing memory
- if a struct has fields that require allocation, a
.init()
method typically is provided that does that - if you need to do a fast initialize without having
init()
do an allocation, you can initialize the struct directly into whatever already allocated memory you might have via.{ }
syntax, rather than calling.init()
For example, you can try this code:
pub fn test_fn() void {
var thing: i32 = 3;
_ = thing;
@breakpoint();
}
test "testing breakpoint" {
test_fn();
}
I called this file "test_bp.zig".
- (optional) put
@breakpoint()
macros in your code where you want to break into the debugger - use the
-femit-bin
argument withzig test
to emit a binary:zig test test_bp.zig -femit-bin=test_bp
. In this example, this should produce a binarytest_bp
. - get the path to the zig executable:
which zig
- run the binary in lldb, passing in the path to the test:
lldb -- ./test_bp `which zig`
After you give lldb the r
command to run the program, you should see something like:
Test [1/1] test "testing breakpoint"... Process 91749 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BREAKPOINT (code=EXC_I386_BPT, subcode=0x0)
frame #0: 0x0000000100003430 test_main`test_fn at main.zig:1:23
-> 1 pub fn test_fn() void {
2 var thing: i32 = 3;
3
4 _ = thing;
5
6 @breakpoint();
7 }
Target 0: (test_main) stopped.