#syntax

1 messages · Page 1 of 1 (latest)

teal adder
#

so i'm learning the basics rn. is:
value: ?*u8 a nullable pointer to u8
value: *?u8 a pointer to nullable u8

tepid trellis
#

correct

jovial pecan
#

Correct

tepid trellis
#

Zig's syntax for types is built in such a way that you can just read the information left-to-right
?*u8 - ? optional, * pointer (to), u8 unsigned 8 bit
*?u8 - * pointer (to), ? optional, u8 unsigned 8 bit

jovial pecan
#

both are the same pointer

#

but the underlying data is different

teal adder
#

thanks
after i get the basic lang figured out i'm probably gonna use zig to figure out implementing basic data structures
probably starting with a linked list or similar

#

kinda new/inexperienced

tepid trellis
teal adder
#

ive touched cpp because college and i dont like

when you say pointer lifetimes i think <'a>

jovial pecan
#

You are not wrong

#

just that now is all manual (Thankfully there are like... 2 states lol)

tepid trellis
#

well, that's the way Rust codifies them. the concepts exist in all low-level languages, but Rust is the only (big) languages that has them as a part of its type system

jovial pecan
#

Inside the same function
Outside the same function

#

(During all the program duration is just outside the same function)

teal adder
#

ig cpp:unique_ptr/shared_ptr has something to do with pointer lifetimes probably?

jovial pecan
#

Kind-of

teal adder
#

i think i probably have a basic idea

jovial pecan
#

Those are automatic memory freeing on sharing semantics

tepid trellis
#

well, just make sure you're not leaking pointers out of their scope

fn oops() *u8 {
    var value: u8 = 0;
    return &value; // oh no! we get a pointer to a value that is dead when the function exits
}
teal adder
#

oh yeah i ran into that one in cpp already
dangling pointer i think the compiler calls it

tepid trellis
#

yes.

#

memory management in Zig is all manual, you as the programmer need to make sure you never get into one of these situations

jovial pecan
#

After some good practices are on you, is rare you hit them

teal adder
#

yeah, i picked zig because of the low level appeal of that kind of thing
i like working with simpler lower abstraction stuff

#

my first programming was on a ti-83 :>

#

the whole no classes thing is different for me though, its been a minute since ive used something without classes. i basically just used them as method structs though so 🤷

tepid trellis
teal adder
#

yeah i was already trying to use unions and structs
what i'm currently at:

pub const linkedList = struct {
    const data = union(dataType) { int: i8, char: u8 };
    const next: *?linkedList = null;

    pub fn at(i: usize) OutOfBounds!linkedList {
        var resultList: *?linkedList = @This();
        for (0..i) |idx| {
            _ = idx; // autofix
            if (resultList.*.next.* == null) {
                return OutOfBounds;
            }
            resultList = resultList.*.next;
        }
    }
    pub fn push(value: dataType) {
        var curElement = @This();
        while(curElement.next.* != null) {
            curElement = curElement.next.*;
        }

    }
tepid trellis
#

there are a few problems in your code, do you want me to point them out?

teal adder
#

im aware there's no return statement in at, i just added it after looking at it
push isnt even close to done yet too

tepid trellis
#

there are a few others...

teal adder
#

what
sidenote, is there a ``` lang tag that highlights okay for zig

tepid trellis
#

alright.
first, your use of @This: @This is a builtin that returns you the type definition you're currently in, if you're inside of linkedList the output of @This would be that type. it's not like the this pointer in C++ or similar.
if you want to get an item, add a self argument to your methods:

pub fn at(self: *linkedList, i: usize)
pub fn push(self: *linkedList, value: dataType)
tepid trellis
teal adder
jovial pecan
#

just syntax sugar =3

#

but yes

tepid trellis
teal adder
#

what do you mean by that

#

what language doesn't have that ❓

tepid trellis
#

some languages require you to use dot syntax, you can't write out the first version

teal adder
#

oh
is there a reason one would need the first version if the second version exists

tepid trellis
#

higher order functions

teal adder
#

sorry
i know that term, but not what it actually means
something something functional programming

tepid trellis
#

that's a bit of a rabbithole, but yes, something something functional programming, it's a good reason though!

teal adder
#

ah
i thought currying was like
some_function(thing)(other_thing)

tepid trellis
#

hmmm... why are you holding the next as a *?linkedList and not ?*linkedList? the current approach requires you to store in memory a ?linkedList for every node, instead of just a linkedList

teal adder
tepid trellis
#

ah ok lol

#

that's a bit of a nitpick, but you should perhaps follow Zig's naming conventions (PascalCase for types, snake_case for variables, etc)

tepid trellis
#

I see that in at's for loop you're discarding the iteration variable. if you don't need it you can simply write out for (0..i) |_|

#

that's what I see for now - ya smart, you seem to know how to do these things.

just remember that Zig's semantic analysis engine is super lazy: if you don't use a piece of code it will not type-check it! make sure to write out a test block for every function you create

teal adder
#

unit testing
something else i need to get in the habit of

#

:]

tepid trellis
#

manual allocations and deallocations are something you gotta be aware of in Zig, no RAII is going to save you!
read into how Zig does that, defers and friends, I'm sure you can manage

teal adder
#

i read the keyword defer in zig.guide
i get that it does something on scope exit but i don't get how that relates to allocations

tepid trellis
#

the idiomatic way to create a value that you need to free when going out-of-scope is to init the value, and stick a defer foo.deinit() right after:

var my_array_list = std.ArrayList(u8).init(allocator);
defer my_array_list.deinit();
teal adder
#

ah

jovial pecan
#

Or if the data is going out but you still wanna free on error is with errdefer

teal adder
#

on the topic of errors
is there a way to pass a value as const

src/structures.zig:35:32: error: expected type '[]const u8', found 'u8'
    expect(std.mem.eql(u8, list.data, 'a'));
                           ~~~~^~~~~
#

array of const u8 expected

tepid trellis
#

you're passing in a singlular u8

teal adder
#

yes

tepid trellis
#

not a slice of them

jovial pecan
#

"a"

#

not 'a'

tepid trellis
#

std.mem.eql is for comparing slices, it seems like you want to compare individual u8s, use list.data == 'a' for that

#

(I also assume list.data has changed since you posted your code, else I'd expect it to error with a different message)

teal adder
#

it has
i decided i can worry about unions later

#

this is throwing something about needing to be known at comptime?

    pub fn push(self: *linkedList, value: u8) void {
        var curElement: ?*linkedList = self;
        while (curElement.?.*.next != null) {
            curElement = curElement.?.*.next;
        }
        curElement.?.*.next = ?*linkedList.init(value);
    }
#
src/structures.zig:28:49: error: unable to resolve comptime value
        curElement.?.*.next = ?*linkedList.init(value);
tepid trellis
#

what's the push function supposed to do?

teal adder
#

add an element to the linked list

tepid trellis
#

to... the end..? of it?

teal adder
#

yes

tepid trellis
#

ok...
in the last line, on the right hand side of the =, you wrote ?*..., you're essentially declaring a type there, since ? and *, when used as prefix operators, make types

teal adder
#

oh
how do i make that line happy then
i can't un ? * on lhs, that throws a different error

tepid trellis
#

Zig tries to create this type (it still does not know that that will be a type mismatch later), and because types can only exist at compile time it tries to call linkedList.init(value) at compile time, and fails because value is runtime known.

#

can you share the body of init? I worry it itself might have some problems...

teal adder
#
    pub fn init(value: u8) linkedList {
        return linkedList{ .data = value };
    }
tepid trellis
#

oh yeah that's gonna cause some problems...

#

can you explain exactly what you're trying to do in the last line of push? I suspect I know what you got wrong...

teal adder
#

i'm trying to assign the pointer next to a new linkedList
every permutation i've tried involving dereferencing, adding a reference on rhs, etc has either errored or panicked

tepid trellis
#

ok
so let's go over this together...

#

let's begin with finding the last element of the linked list; what you did is alright, but we can do somewhat better...
your variable, curElement, is of type ?*linkedList - why is that? we know the pointer points to an existing node, so *linkedList would work just fine (I also took the liberty to use snake_case):

var cur_element: *linkedList = self;
#

and because of type inference we can leave out the type annotation:

var cur_element = self;
#

now for the iteration...
you check if the next value isn't null like so: cur_element.?.*.next != null
because cur_element is now not optional we need not the .?, arriving at cur_element.*.next != null
but we can do better...

#

first, Zig has some syntax sugar for getting a field out of a pointer-to-struct. if I have a foo variable of type *Foo and I want to access it's bar field, I can write it like foo.*.bar, and also as foo.bar - Zig inserts the dereference

so we write cur_element.next != null

teal adder
#
    pub fn push(self: *linkedList, value: u8) void {
        var curElement = self;
        while (curElement.next != null) {
            curElement = curElement.next.?;
        }
        curElement.next.? = linkedList.init(value);
    }
tepid trellis
#

we can eliminate the boolean check for null:
if you have a nullable value, and you want to execute one branch if it is non-null, and another if it is, we can write:

if (nullable_value) |non_null| {
    // something
} else {
    // something else
}

if the type of nullable_value is ?i64, the type of non_null will be i64, and will contain the unwrapped value in case the argument is non-null

this is if with optionals

#

this also works with while loops, so we can write it like so:

var cur_element = self;
while (cur_element.next) |next| {
    cur_element = next;
}
// ...
#

no need for explicit unwrapping!

#

and for the last part... appending a value...

teal adder
#

thought if/while only evaluate booleans and error unions

tepid trellis
#

notice the capture (|non_null|) at the end

teal adder
#

ig next is a union?

#

okay

#

right

#

is it only "special" unions you can do that with

tepid trellis
#

cur_element.next needs to be set with a value of type ?*linkedList (or just a *linkedList, that will type-coerce into an optional)
where do we get that pointer-to-linked-list from? init returns us a linkedList, not a pointer thereof...

to reference a value, and get a pointer pointing at it, use the addr-of operator: &

I'll let you try and finish the code with that knowledge, do send it here, because you most likely will have problems

teal adder
#

i tried that and got a const complaint

tepid trellis
#

say... do you know Rust? I can explain how Zig's ? relates to Rust's Option

teal adder
#

rust ? is unwrap_or_else return err

#

i know rust syntax, that's about it

tepid trellis
#

not Rust's ? operator (that's actually Zig's try!)
I mean the optional type, and pattern matching

teal adder
#

yeah,

match thing {
  Some(1) => {return Err(something::terrible::happened},
  _ => 0,
  None() => 0
}
tepid trellis
#

when we want to pattern-match on an optional in Rust we write:

// Rust
match x {
    Some(deffo_x) => todo!(),
    None => todo!(),
}

or with if let:

// Rust
if let Some(deffo_x) = x {
    todo!()
} else {
    todo!()
}

the 2nd version is nearly exactly Zig's unwrapping:

// Zig
if (x) |deffo_x| {
    // TODO
} else {
    // TODO
}
teal adder
#

rust compiler will warning you on those paren on the top

tepid trellis
#

oh yeah lol

#

doing too much Zig recently...

#

in any case...
we got to this situation:

pub fn push(self: *linkedList, value: u8) void {
    var cur_element = self;
    while (cur_element.next) |next| {
        cur_element = next;
    }
    cur_element.next = // ???
}

what do you think we need to do now? even if your code errors, what did you come up with?

teal adder
#

& but that complains that const was not expected

tepid trellis
#

can you send the code?

teal adder
#
    pub fn push(self: *linkedList, value: u8) void {
        var curElement: *linkedList = self;
        while (curElement.*.next != null) {
            curElement = curElement.*.next.?;
        }
        curElement.*.next.? = &linkedList.init(value);
    }
tepid trellis
#

did you change init by any chance?

#

ah no

#

alright

teal adder
#

no, i tried having the init return the ref too but it had the same complaint about const

#

if it was rust:

    pub fn push(self: *linkedList, value: u8) void {
        var curElement: *linkedList = self;
        while (curElement.*.next != null) {
            curElement = curElement.*.next.?;
        }
        curElement.*.next.? = &mut linkedList.init(value);
    }
tepid trellis
#

ok so, what we have here is that a temporary value that we get from linkedList.init(value). that value, due to its temp-ness, is constant, and so using & on it will yield a *const

teal adder
#

so what i need to use some allocate to heap function in std?

#

google time

tepid trellis
#

what we need to do it set it as it's own variable, and reference that, this will give us a mutable pointer:

var node = linkedList.init(value);
cur_element.next = &node;
#

but as you figured out already this causes some lifetime issues (hooray! 🎉)

teal adder
#

danglingPointerException

tepid trellis
#

not even that!

#

best case scenario we get a segfault

#

moving on...
we'll have to allocate some space for the new node we're creating - this requires the use of allocators

#

if we were in C land, we could have used malloc. but in Zig we don't have any omniscient global allocator laying around - we gotta get one somehow

teal adder
#

reading on allocators rn
std.heap.page_allocator?

tepid trellis
#

using the page allocator is probably a bad choice...

#

I think it's best if you take some time to read on allocators, and after you'll know how to solve the problem

teal adder
#

segfault

tepid trellis
#

can you share the code?

teal adder
#

was copying it

#
    next: ?*linkedList = null,
    pub fn init(value: u8) !*linkedList {
        var arena = heap.ArenaAllocator.init(heap.page_allocator);
        defer arena.deinit();
        const allocator = arena.allocator();

        var result: *linkedList = try allocator.create(linkedList);
        result.data = value;
        return result;
    }

    pub fn at(self: *linkedList, i: usize) error{OutOfBounds}!linkedList {
//
    }
    pub fn push(self: *linkedList, value: u8) !void {
        var curElement: *linkedList = self;
        while (curElement.next) |next| {
            curElement = next;
        }
        curElement.next.? = try linkedList.init(value);
    }
tepid trellis
#

yeahhh

#

I see the problem

#

so.

#

lemme explain allocators real quick...

teal adder
#

removed defer, probably not wanted in this case
still segfaults

tepid trellis
#

Zig really wants you to be explicit about where your code allocates dynamic memory
sometimes it's quite cucumbersome, but Zig is built for optimal code, and so we live with it...

there are a few allocators that exist in the standard library: std.heap.GeneralPurposeAllocator, std.heap.page_allocator, etc
those actually hold inside the data required to manage the memory, for a GPA that will be a bunch of info on the different buckets and many other smart things.

we, as people who just want to use an allocation strategy, shouldn't use any of those directly. instead, we should request an allocator argument to our functions, of type std.mem.Allocator

a std.mem.Allocator is a pair of pointers that we got from some allocator implementation, and allows us to call the allocation/deallocation functions of said implementation

#

in your case, for example, the function push needs to allocate memory, and so we'll add an allocator argument:

pub fn push(self: *LinkedList, value: u8, allocator: std.mem.Allocator) error{OutOfMemory}!void
#

also notice the possible error - for the case when the allocation failed

teal adder
#

okay, so init should take an allocator as an argument then too

tepid trellis
#

for now let's make all allocations externals. the linked list will not hold any std.mem.Allocator values inside

teal adder
#

yep, writing it that way rn

#

the test will make an allocator

tepid trellis
#

both push and init need an allocator argument, yes

#

inside of test blocks you can use std.testing.allocator; it'll check for any leaked memory / double frees / other bugs

teal adder
#

my allocator is complaining about missing argument. the argument wanted is self

tepid trellis
#

send the error message please?

teal adder
#
src/structures.zig:40:45: error: expected 1 argument(s), found 0
    var list = try linkedList.init('a', gpal.allocator());
                                        ~~~~^~~~~~~~~~
/snap/zig/11356/lib/std/heap/general_purpose_allocator.zig:302:13: note: function declared here
        pub fn allocator(self: *Self) Allocator {
        ~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(00:07:59)(kodi)(~/Docum/proje/zig-datastructures)(1)zig test ./src/structures.zig
src/structures.zig:40:45: error: expected 1 argument(s), found 0
    var list = try linkedList.init('a', gpal.allocator());
                                        ~~~~^~~~~~~~~~
/snap/zig/11356/lib/std/heap/general_purpose_allocator.zig:302:13: note: function declared here
        pub fn allocator(self: *Self) Allocator {
        ~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(00:08:40)(kodi)(~/Docum/proje/zig-datastructures)(1)zig test ./src/structures.zig
src/structures.zig:39:27: error: expected 1 argument(s), found 0
    const allocator = gpal.allocator();
                      ~~~~^~~~~~~~~~
/snap/zig/11356/lib/std/heap/general_purpose_allocator.zig:302:13: note: function declared here
        pub fn allocator(self: *Self) Allocator {
tepid trellis
#

ummm
what's gpal and where is it defined?

teal adder
#
const gpalc = heap.GeneralPurposeAllocatorConfig{ .safety = true };
    const gpal = heap.GeneralPurposeAllocator(gpalc);
    const allocator = gpal.allocator();
    var list = try linkedList.init('a', allocator());
    try list.push('b', allocator());
    try expect(list.data == 'a');
    if (list.at(1)) |ll| {
        try expect(ll.data == 'b');
    } else |e| {
        std.debug.print("{any}", .{e});
    }
tepid trellis
#

make gpal var, and remove the () after uses of allocator

teal adder
#
src/structures.zig:38:9: error: variable of type 'type' must be const or comptime
    var gpal = heap.GeneralPurposeAllocator(gpalc);
        ^~~~
tepid trellis
#

ah, pff
yes. add {} after heap.GeneralPurposeAllocator(gpalc)

teal adder
#

compiles but segfault

jovial pecan
#

what's the trace

teal adder
#

in push, while(curElement.next)

tepid trellis
teal adder
#
Test [1/1] structures.test.ll... General protection exception (no address available)
/home/kodi/Documents/projects/zig-datastructures/src/structures.zig:29:26: 0x1039526 in push (test)
        while (curElement.next) |next| {
                         ^
/home/kodi/Documents/projects/zig-datastructures/src/structures.zig:41:18: 0x103971e in test.ll (test)
    try list.push('b', allocator);
                 ^
/snap/zig/11356/lib/compiler/test_runner.zig:158:25: 0x10479b2 in mainTerminal (test)
        if (test_fn.func()) |_| {
                        ^
/snap/zig/11356/lib/compiler/test_runner.zig:35:28: 0x103dd1b in main (test)
        return mainTerminal();
                           ^
/snap/zig/11356/lib/std/start.zig:501:22: 0x103a069 in posixCallMainAndExit (test)
            root.main();
                     ^
/snap/zig/11356/lib/std/start.zig:253:5: 0x1039bd1 in _start (test)
    asm volatile (switch (native_arch) {
    ^
???:?:?: 0x0 in ??? (???)
tepid trellis
#

can you send the code of push and init?

jovial pecan
#

curElement.netxt.? will crash if next is null

tepid trellis
#

it seems like you're not initialising the nodes correctly...

tepid trellis
teal adder
#

no

jovial pecan
#

and while(curElement.next) can be null and skipped

teal adder
#
 pub fn push(self: *linkedList, value: u8, allocator: std.mem.Allocator) !void {
        var curElement: *linkedList = self;
        while (curElement.next) |next| {
            curElement = next;
        }
        curElement.next.? = try linkedList.init(value, allocator);
    }
#

not all that much

tepid trellis
#

and init?

teal adder
#
    pub fn init(value: u8, allocator: std.mem.Allocator) !*linkedList {
        var result: *linkedList = try allocator.create(linkedList);
        result.data = value;
        return result;
    }
tepid trellis
#

I see.

zinc gust
#

next is unitialized

tepid trellis
#

you're not setting result.next

teal adder
#

i forgot default init doesn't exist

#

pff

tepid trellis
#

result.next has garbage data, and so it's both not considered null, and points to some bad place in memory

teal adder
#

attempt to use null value
progress
fixed
test passes

#

sorry about all that

tepid trellis
teal adder
#

the ? is what gave the attempt to use null value
its removed

tepid trellis
#

alright.
can you send the entire definition of linkedList? so we can go over everything?

teal adder
#
const std = @import("std");
const expect = std.testing.expect;
const heap = std.heap;

const OutOfBounds = error{OutOfBounds};

const linkedList = struct {
    data: u8 = 0,
    next: ?*linkedList = null,

    pub fn init(value: u8, allocator: std.mem.Allocator) !*linkedList {
        var result: *linkedList = try allocator.create(linkedList);
        result.data = value;
        result.next = null;
        return result;
    }

    pub fn at(self: *linkedList, i: usize) error{OutOfBounds}!linkedList {
        var resultList: ?*linkedList = self;
        for (0..i) |_| {
            if (resultList.?.*.next == null) {
                return error.OutOfBounds;
            }
            resultList = resultList.?.*.next;
        }
        return resultList.?.*;
    }
    pub fn push(self: *linkedList, value: u8, allocator: std.mem.Allocator) !void {
        var curElement: *linkedList = self;
        while (curElement.next) |next| {
            curElement = next;
        }
        curElement.next = try linkedList.init(value, allocator);
    }
};

test "ll" {
    const gpalc = heap.GeneralPurposeAllocatorConfig{ .safety = true };
    var gpal = heap.GeneralPurposeAllocator(gpalc){};
    const allocator = gpal.allocator();
    var list = try linkedList.init('a', allocator);
    try list.push('b', allocator);
    try expect(list.data == 'a');
    if (list.at(1)) |ll| {
        try expect(ll.data == 'b');
    } else |e| {
        std.debug.print("{any}", .{e});
    }
}
#

a few things need improved
my return types on push and init, for example

tepid trellis
#

use std.testing.allocator inside of the test block. you'll find some more bugs hiding...

#

so, replace the first 3 lines there with

const allocator = std.testing.allocator;
teal adder
#

i did

#

it says it found a memory leak twice
one bit apart
hmm

#

im returning the memory leak aren't i

tepid trellis
#

watcha mean?

#

if you're returning it, it's not a leak

teal adder
#

yeah

#

i dont get how i'm leaking with init

tepid trellis
#

but in some other place you're forgetting about memory - there hides the leak

teal adder
#

how is this leaking?

/home/kodi/Documents/projects/zig-datastructures/src/structures.zig:12:55: 0x1039197 in init (test)
        var result: *linkedList = try allocator.create(linkedList);
                                                      ^
/home/kodi/Documents/projects/zig-datastructures/src/structures.zig:41:35: 0x10394ad in test.ll (test)
    var list = try linkedList.init('a', allocator);
#

that's the memory i intended to allocate?

tepid trellis
#

the allocator is allocating alright - but you never free the allocated memory

#

the problem is that you never defined a deinit function

#

try to create a deinit function, that takes in a *LinkedList and a std.mem.Allocator (we expect the allocator that is passed to be the same one that was used in push and init), and frees all of the nodes from the list

teal adder
#

its becoming a double linked list

tepid trellis
#

there is no need to doublify the linked list

jovial pecan
teal adder
#

ig i keep a reference to the previous node and deinit behind me

tepid trellis
#

exactly

jovial pecan
teal adder
#
    pub fn deinit(self: *linkedList, allocator: std.mem.Allocator) void {
        var prev_node = self;
        while (prev_node.next) |next| {
            allocator.destroy(prev_node);
            prev_node = next;
        }
        allocator.destroy(prev_node);
    }
jovial pecan
#

and if you are removing at a position, you remove it from that position and link the previous with the next one (traversing from start to finish)

teal adder
#

it passes

tepid trellis
#

where did you put the deinit exactly?

tepid trellis
teal adder
#
    const allocator = std.testing.allocator;
    var list = try linkedList.init('a', allocator);
    defer list.deinit(allocator);
tepid trellis
#

you can look at maybe revising your at implementation (similar changes to what was done to the loop in push), and maybe-just-maybe make your names adhere to the style guide.

other than that, I think your code is complete

teal adder
#

its complete as a very basic implementation
a lot more can be done for a linked list
drop()
from_type()
idk

tepid trellis
#

you can add whatever, the current API seems correct and bug-free
I approve👍

teal adder
#
    pub fn at(self: *linkedList, i: usize) error{OutOfBounds}!*linkedList {
        var resultList = self;
        for (0..i) |_| {
            if (resultList.next) |next| {
                resultList = next;
            } else {
                return error.OutOfBounds;
            }
        }
        return resultList;
    }
tepid trellis
#

spot on

teal adder
#

ill make the outer facing stuff match style guide at least

#

rip
search replace missed anything with a star behind it

#

wait, this isnt
i didnt write changes before cat

#

lsl

tepid trellis
#

the declaration of LinkedList needs to be pub, if you want to expose it: pub const LinkedList = ...

teal adder
#

ik
it's all in file rn
it'll become pub if i decide i want to actually use it
i can't even call main without pub

#
const std = @import("std");
const expect = std.testing.expect;
const heap = std.heap;

const OutOfBounds = error{OutOfBounds};

const LinkedList = struct {
    data: u8 = 0,
    next: ?*LinkedList = null,

    pub fn init(value: u8, allocator: std.mem.Allocator) !*LinkedList {
        var result: *LinkedList = try allocator.create(LinkedList);
        result.data = value;
        result.next = null;
        return result;
    }
    pub fn deInit(self: *LinkedList, allocator: std.mem.Allocator) void {
        var prev_node = self;
        while (prev_node.next) |next| {
            allocator.destroy(prev_node);
            prev_node = next;
        }
        allocator.destroy(prev_node);
    }

    pub fn at(self: *LinkedList, i: usize) error{OutOfBounds}!*LinkedList {
        var result = self;
        for (0..i) |_| {
            if (result.next) |next| {
                result = next;
            } else {
                return error.OutOfBounds;
            }
        }
        return result;
    }
    pub fn push(self: *LinkedList, value: u8, allocator: std.mem.Allocator) !void {
        var cur_element: *LinkedList = self;
        while (cur_element.next) |next| {
            cur_element = next;
        }
        cur_element.next = try LinkedList.init(value, allocator);
    }
};

test "ll" {
    //const gpalc = heap.GeneralPurposeAllocatorConfig{ .safety = true };
    //var gpal = heap.GeneralPurposeAllocator(gpalc){};
    const allocator = std.testing.allocator;
    var list = try LinkedList.init('a', allocator);
    defer list.deInit(allocator);

    try list.push('b', allocator);
    try expect(list.data == 'a');
    if (list.at(1)) |ll| {
        try expect(ll.data == 'b');
    } else |e| {
        std.debug.print("{any}", .{e});
    }
}

pub fn main() void {
    std.log.info("{any}", .{@sizeOf(LinkedList)});
}
#

never liked snake case
more annoying to type than camelCase

jovial pecan
#

in windows was it wWinMainCRTStartup

#

Tho _start does have to be public 🙂

teal adder
#

anyway, gn
its 1 am here

jovial pecan
#

4am 🧠