#Please help me understand Zig, I am trying to not use AI
1 messages · Page 2 of 1
make an emu
^
if this tests exists somewhere you should run it as a rom
I'm talking about your code
the original one has a lot of forbidden regions/ mirrored regions, etc etc
rn there isnt any difference, once I start implementing more it will break
the read methods are the same. the write methods are the same
what is diff
is it the values in bytes that will be diff?
yeah only for now, because I havent started implementing the memory regions
Also, considering that the gameboy is in a different arch, why are you attempting to directly map its memory space to x86_64's memory space?
yeah
The true validation on an emulator are test roms imo
I do that for my NES emulator
test roms are there, but I am doing this https://github.com/singlesteptests/sm83
Wait, I think I understand what is going on now
I cant test test roms until i have implemented more stuff
you can add it as test-only method to the struct, or you can just do it manually in the test - no private fields, take advantage :P
Yeah, there is no reason to test the emulator under a fake condition
It just makes the test unreliable
you bigly overthinking it
initialization isnt the only different part, some regions are read only, some are mirrored. The mocked memory shouldnt have those
this is just a cpu test, it doesnt test the entire emu
At best, it is not useful, at worst, it drives bad design decisions
The behaviour of the CPU is not independent of the memory space
it is
i am trying to verify whether the cpu implemntation is working or not
not how it interacts with the rest of the system
what does it mean for something to be read-only. where is the information stored that says it is that
I agree it isn't, but you can only test it with real program imo
You are emulating, you don't really have the memory mock issue
is it stored in the mocked memory itself, or in the emulated hardware?
prohibited regions
I am talking about the design of your program
I understand emulators, I am trying to understand what you are doing
because the problems you are describing don't make sense to me
read only means I ignore writes in that address space
then why can't just do that for the real virtual mem structure
Nothing stops you to use prohibited regions if no one uses it for nothing btw
You can not emulate them (Wrong) or emulate them and have UB behaviour (Kinda ok) or search what happened in games on that region
You cannot make this claim without knowing the ISA
It may very well be the case that certain memory accesses correspond to different CPU behaviours
my problem is simple:
- I have a component A which has a dependency B
- I need to mock the internals of dependency B for a specific test of A
That is all I need
In an arch such as the Gameboy's, this would make sense
Why would you separate them
talking about gameboy specific stuff or my emulator design is not the goal
is my question
As the whole system is very closely tied together
how do i achieve this in zig
We generally don't like X Y questions =(
Because at hand here is a bigger issue
Why you need to mock them
It is not clear
Note, I have also written emulators, and I develop for the gba for fun
Anyways, you are designing an emulator, not a hardware component. Treating things as independent from each other is an unnecessary goal
i need the component to behave differently in the test than it would do originally
Why?
the test requires it
The point is to accurately run ROMs
Feels like outside the point
You do not need the component independence assumption for that
can i do this in zig or not?
The test might require whatever, just skip the test, test yourself the same assumption if you want static guardrails
the answer is yes, but we see at as pointless
if you really want to do this: make it generic over the type. shitass solution, because then you're not really testing that it works. but go off
or make it a runtime vtable
pick yer poison
Not to mention that because Zig is not an HDL, you will most likely harm your program over this (in terms of performance)
to be clear, that is how "mocking" works in all languages
I cant skip the test
hmm not looking for a runtime perf impact so probably not that
This vtable would have minimal runtime perf hits
a mock is just a way to inject some code that does something differently. all other languages do this using things like interfaces, vtables, generics, w/e. zig does not do these in as "pretty" ways, so it's going to look as shitass as it is
but the better way is rewrite the test to your constraints
Or make CPU truly separate from memory and Create the Test Suite with Custom Memory types
But this is gonna make you write specific code as a handler for the CPU test
making the cpu separate from memory like passing it explicitly everywhere?
And you will miss integration issues later on if you don't test those
Not necesarily
Just CPU is it's own type
and doesn't know about memory
it knows maybe about the BUS
I think you should show roughly the code you want to run, the stage at which you encounter the problem and what is your expected desired outcome is. Otherwise, it is quite hard to help most of programmers are highly visual and need to see the code in front of them to understand what is going on precisely 👍
Less so than this, I think most of us understand it, is just... baffling ig
We won't provide code per se either
Tbh, I don't think designing things that independently frpm each other makes sense in an emulator
As i said, it is not hardware design
It is not, but it has it's merits
idk why zig doesnt plan to have a more convenient way to do interfaces when its probably one of the most important concepts in programming since functions and classes
OOP is not an objective
Because there are a lot of ways to design interfaces
And Zig should not prefer one over the other
Because each one has its own merits
being important does not mean it has to be convenient to do. making it convenient to do means endorsing it, and zig doesn't endorse needless abstractions like the one you want
In this case for this specific code with the intentions of this specific user I would:
- Create a standalone CPU emulator with callbacks for memory writes
- Create a Test Handler for the CPU bit of the equation, it loads the test from the jsons and creates a highly standarized set of test
- Since Mem is abstracted as read/write via callbacks, the compiler can inline this calls easily, specially if it's comptime known
- You then create you Emulator that holds mem and cpu, CPU calls the callbacks of mem that you init at the start of your emulator
this way you get a closure over the set of memories

I thought zig liked having a single way of doing things, instead of everyone doing stuff their own way
That applies to things that do not have actual reasons for being different
comptime polymorphism vs runtime polymorphism, for example, is not just a styling preference
each one has actual usecases and situations where it is preferable over the other
it aims to have one obvious way to use the language, not a singular way to do every single thing. programming is a field rife with seemingly minimally different, but importantly distinct approaches to many things, with lots of different tradeoffs. it is impossible to literally only have "one" way to do absolutely everything, and generics/interfaces/polymorphism is one where there are so many different tradeoffs that, even beyond the concern of not wanting to endores needless abstraction, it would be irresponsible as a low level language to endorse one specific model for this kind of abstraction, when such a wide berth of approaches exist
I mean rust does this with Traits and dyn Traits, you can make it easier without having to put one path down
zig ain't rust
different design priorities
rust embraces super complex abstraction
zig encourages thinking about the particulars a lot before even considering abstractions
in this case rust locks you into one way to have the language take away from you how monomorphization and dynamic dispatch actually works
In this case, you can move on and just abstract over two functions (if you want you can pas the vtable at comptime)
I feel like zig is a lot about not taking away power from the user for no reason
Callbacks are fun
like closures, why do you need to take a ctx object by hand in zig? because look at rusts FN types and the issues and confusion it causes
and the hidden performance issues
also is there a way to do inclusive range in for loops
a lot of zig is doing things by hand because the solution is NOT as obvious as a lot of languages make it seem
no. Just add one to your range
pub fn step(
self: *Cpu,
comptime Memory: type,
comptime readFn: fn(ctx: *Memory, addr: u16) u8,
comptime writeFn: fn(ctx: *Memory, addr: u16, val: u8) void,
ctx: *Memory
) void {
const opcode = readFn(ctx, self.pc);
self.pc += 1;
// handle opcode...
}
use codeblocks
I fucking know
else this is unreadable
for (a..b + 1) |inclusive| {}
i actually dislike this, the proximity makes it read as range + int for me
i would, even tho its pointless, write this as a..(b+1)
bre
this doesnt feel clean
I mean, considering that range + int is not a valid operation in Zig, I don't see how one might come to the conclusion
because its not, the code looking clean doesnt make the implementation less complex
tests are not intended to emit output, they are intended to test conditions. if you want to debug stuff, you can use std.log.err (note that reaching a call to this will also mark the test as failed)
hiding complexity is not equal better code
if the complexity is relevant
my programming teacher always said "you cant hide complexity" it will show up somewhere
he religiously mentioned this every lesson
law of leaky abstractions moment?
You can also make CPU a generic that takes a memory type with a duck typed interface
But
This is as minimal you can get without incurring perf issues
The friction is because you are doing all this for a thing you can manually replicate in your own model and adapt
but you are not adapting, so we gotta get creative
I still do not get why you want to have all components be strictly separate
you are making an emulator to run ROMs, you are not designing hardware
i mean i can just do it normal rn, the test will just break in future once the memory stuff gets more implementation
YAGNI?
you aint gonna need it
You Aren't Going to Need It
you write a breaking test, you implement code until it works, then you write a breaking test again
that's the test driven development
https://ziglang.org/documentation/0.16.0/ why cant i find any docs about std.json here
you should not design tests that pass implemented code
you should write code that passes created tests
that's the langref
oh
if you look at the first paragraph, it links to the stdlib docs
oh my bad
all good
you can also have them locally with zig std
(it runs a little local server that you can see in the browser)
tests, after all, represent the expectations over the code. They should treat the code like a black box
If you want, you can take the cpu tests and take the assumptions of memory out
and so make them your own
exposing implementation details like the degree of separation of components is simply bad test design
its like 10000+ tests for each opcode (200+) so it will be hard
wait maybe not that many
They are JSON, you can parse them and out code based on it
how do i create a formatted string?
trying to look into std.fmt
but cant find the exact function
yeah, std.fmt.bufPrint
depends on where the string is going
but isnt that print
if you need it to be dynamically allocated, you can use fmt.allocPrint
wdym
it's print to buffer!
i basically need to construct a filename and then check if that file exists
i thought print meant print to a console
Could always use std.mem.concat depending on what you actually need
no, print literally just means to print a human readable string
to either a fixed buffer or a file
or an array list
the console is just a file descriptor at the end of the day 
maybe in other langs. in zig we just use it to mean "uses std.Io.Writer.print"
I think for windows it is a bit weirder than that
and writers can print to anywhere, including to in-memory buffers
Tell me, what isnt weird in windows?
fair
Even getting stdout/stderr to the terminal is hell on earth
basically need this:
for i in 0..0xFF {
file_name = "{i}.json";
if file_name exists {
read file_name and do stuff
}
}```
Linux/Mac/FreeBSD:
./x
Windows:
.\x.exe 2>&1 | %{if($_.Gettype().name -eq "ErrorRecord"){$_.Exception}else{$_}} | Tee-Object -FilePath ./log.txt
well, depending on what you're doing bash can be pretty similarly ass
but ye
I think is not the place in this user post =3
Fair 
Yeah bufPrint is all you need then
Getting the colors (ansi) to work in the terminal and making it comfortable to type was actually a nightmare true, but what a result!!
try app_log.print(.info, .{ "built for ", .os_arch, " on ", .timestamp, .nl }, .{
zterm.TagConst(.os_arch, "{s} {s}", comptime .{ @tagName(builtin.os.tag), @tagName(builtin.cpu.arch) })
.setColor(zterm.pallete.sky_magenta),
zterm.Tag(.timestamp, "{d:04}.{d:02}.{d:02}T{d:02}:{d:02}:{d:02}Z", .{
irl_date.year,
irl_date.mon,
irl_date.day,
irl_time.hour,
irl_time.min,
irl_time.sec,
}).setColor(zterm.pallete.sky_magenta),
});
const max = 0xFF;
for (0 .. max + 1) |i| {
var path_buf: [std.fmt.count("{d}.json", .{max})]u8 = undefined;
const path = std.fmt.bufPrint(&path_buf, "{d}.json", .{i}) catch unreachable;
const file = try dir.openFile(io, path, .{});
defer file.close(io);
// whatever
}
var buf: [32]u8 = undefined;
for (0..0x100) |ins| {
const file_name = try std.fmt.bufPrint(buf, "{}.json", .{ins});
std.log.err("{s}", file_name);
}
``` is this how you use it? sorry kinda new to all this buffer and pointer stuff
that would work too ye
Huh... i didnt know fmt.count was a thing...
Ima steal that 
oh thanks
I wrote it in a way that statically guarantees the buffer is exactly as big as it needs to be
and note: declaring the buffer inside or outside the loop doesn't matter
but I think it's nice to keep it close to where it's needed
I see a potential bug.
He is not setting the buffer to undefined after each iteration
❯ zig build test --summary all
test
└─ run test zigb
└─ compile test zigb Debug native 2 errors
src/Cpu.zig:107:9: error: local variable is never mutated
var file_name: [32]u8 = undefined;
^~~~~~~~~
src/Cpu.zig:107:9: note: consider using 'const'
src/Cpu.zig:110:26: error: array literal requires address-of operator (&) to coerce to slice type '[]u8'
std.fmt.bufPrint(file_name, "{}.json", .{ins});```
that doesn't matta
i get this error
that can never be the bug
they don't implicitly coerce to slices until you take em by ref
Hm... i wonder why i had issues with it in previous versions, but i digress
if it was 0 terminated then you may need to fill it with zeroes
but other than that
you're probably thinking of something else, like using the buf directly instead of the returned slice
oh silly me
Probably now that you mention it (why did i do it tho is beyond me rn)
even then only a zfill is sensible, not undefined
i thought declaring outside wouldnt reallocate it every loop
No
it's just a stack buffer
Any buffer is safe to reuse, zig doesn't allocate behind your bag
so what you use is what you get
at worst, that would entail incrementing and decrementing the stack pointer
and realistically, it just doesn't do anything because the codegen will just allocate the whole stack frame needed
oh ok
no?
i mean yeah, obviously you need neither zfill nor undefined fill if you know how large the thing you wrote is. i meant specifically when you use nullterm
btw why do catch unreachable instead of try
if it was 0 terminated then you may need to fill it with zeroes
but other than that...
It's only problematic if you have memory assumptions in the system @plucky niche
yeah
because I wrote it in a way that the OutOfSpace error is veritably, and obviously impossible.
so I catch unreachable as an assertion of that fact
you never need to override for non terminell terminated stuff, that was my point
whats dir and io here exactly? how do i access them inside a test
for tests, std.testing.io should suffice
dir is just the directory handle where you expect these json files to be inside
if they're in cwd, std.Io.Dir.cwd()
if they're in some subdir, you can open it with like std.Io.Dir.cwd().openDir
I would personally probably pass their path in as a build option
oh ok
once you run the command the file doesn't exist anymore, it's been compiled into the binary n allat
how do i exactly do that?
it's a build system thingie, see b.addOptions()
https://ziglang.org/learn/build-system/, the "Options for Conditional Compilation" section
to be clear, I'm not saying you have to do this
but this is what I would do because I'm a perfectionist who doesn't like relying on relative paths in projects n all that
it has a lot of things I have never used in any other languages
don't apologise, that's what this forum is for
I appreciate that you are using it in the first place
ever since I've founded it as a forum I get nothn but complaints about having to use it smh
I will complain more ink
=P
its nice to have a place where people can focus on a topic without disrupting normal conversation in general
when Ink and I started helping on zig help back in 0.8 days, it was a single channel
it was awful have to say
yeah, very difficult to keep up with so many questions in one thread of conversation
(discord's forums aren't really perfect either tbh, I long for zulip threads)
And we were fast answerers lol
them's the days
is there a way to define functions inside a test
test "something" {
fn process_test_file(file_name: []u8) io.File.OpenError!void {
const file = try dir.openFile(io, file_name, .{});
defer file.close(io);
}
...
}
cause this isnt working
same way as defining functions in any other functions
test "something" {
const nice_name = struct { // like "helper" or something
fn foo() void {}
};
nice_name.foo();
}
oh ok
imo, it would be even more unsettling if no one complaints... 
can anyone tell me how do i read a json file in zig
I am reading the documentation but its not clear to me
Are you talking abour reading file, or parsing JSON string?
i assume reading a json file and parsing it
so i read a File, its a json file, I need to work with it
so how do i do that
do you know about reading a file already?
yeah
const file = dir.openFile(io, file_name, .{}) catch continue;
defer file.close(io);
how do i work with file as a json file
do you want to fully load the file into memory before parsing?
how do i use an allocator
well this might suite better
https://ziglang.org/documentation/master/std/#std.json.static.parseFromSlice
hmm sure? idk
you can either use the API function that takes in a std.Io.Reader (which is your file reader) or you can just parse the whole text once you have it in memory
fn process_test_file(file: std.Io.File) !void {
// do stuff with file.json
}
trying to implement it like this
i don't recommend passing File it self, as it needs IO to do anything on the file handle anyway
pass reader or raw bytes
ummm how do i do that
i fucking hate windows 11, my computer hard-froze
I do not understand how to use an allocator
just pass allocator you have in init
you either initialize an allocator at the start of your program and pass it down the rest of your program, or you take it off juicy main
you're probably gonna take it off juicy main (which is copying init.gpa)
I am using this in a test
don't be scared of allocators, you don't even have to use it, just pass it where its necessary
std.testing.allocator
then for the moment, it's best if you use std.testing.allocator, yeah
oh ok
i'm pretty sure the documentation has a section for picking your allocator, hang on
yup
thanks
i dont understand something, why does dir.readFile take a buffer and also return a buffer
like what
test "sm83-single-step-tests" {
const io = std.testing.io;
const dir = try std.Io.Dir.cwd().openDir(io, "tests/sm83-ssts/v1", .{});
const allocator = std.testing.allocator;
// load test .json;
// for test in test.json:
// set initial processor state from test;
// set initial ram state from test;
//
// for cycle in test:
// cycle processor
// if we are checking cycle-by-cycle:
// compare our r/w/mrq/address/data pins against the current cycle;
//
// compare final ram state to test and report any errors;
// compare final processor state to test and report any errors;
const helper = struct {
fn process_test_file(file: []u8) !void {
}
};
for (0..0xff + 1) |ins| {
var path_buf: [32]u8 = undefined;
const file_name = try std.fmt.bufPrint(&path_buf, "{x:02}.json", .{ins});
const file = dir.readFile(io, file, ??? ) catch continue;
helper.process_test_file(file);
if (ins == 0xCB) {
for (0..0xFF + 1) |sub| {
const sub_file_name = try std.fmt.bufPrint(&path_buf, "{x:02} {x:02}.json", .{ ins, sub });
const sub_file = dir.readFile(io, sub_file_name, ??? ) catch continue;
helper.process_test_file(sub_file);
}
}
}
}
this is basically what I am trying to do
am i doing it right?
would you happen to know the usecase for brkallocator
i still dont get it
WASM and embedded
const file_buffer = try allocator.alloc(u8, 500 * 1024); // 500 KiB
defer allocator.free(file_buffer);
for (0..0xff + 1) |ins| {
var path_buf: [32]u8 = undefined;
const file_name = try std.fmt.bufPrint(&path_buf, "{x:02}.json", .{ins});
const file = dir.readFile(io, file_name, file_buffer) catch continue;
try helper.process_test_file(file);
if (ins == 0xCB) {
for (0..0xFF + 1) |sub| {
const sub_file_name = try std.fmt.bufPrint(&path_buf, "{x:02} {x:02}.json", .{ ins, sub });
const sub_file = dir.readFile(io, sub_file_name, file_buffer) catch continue;
try helper.process_test_file(sub_file);
}
}
}
``` ok this works nicely
am i using the allocator right?
yea
test
└─ run test zigb
└─ compile test zigb Debug native 1 errors
/usr/lib/zig/std/mem/Allocator.zig:444:50: error: access of union field 'pointer' while field 'union' is active
const slice_info = @typeInfo(@TypeOf(memory)).pointer;
~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~
/usr/lib/zig/std/builtin.zig:550:18: note: union declared here
pub const Type = union(enum) {
^~~~~```
what does this error mean
fn process_test_file(file: []u8) !void {
const value = try std.json.parseFromSliceLeaky(std.json.Value, allocator, file, .{});
defer allocator.free(value);
}
I am just doing this
value is not a pointer. I think you're not supposed to free a json.Value, you pass an arena allocator and deinit/reset it
my program just gets stuck idk why
(value is not a pointer, so allocator.free(value) can't compile)
no i took care of that
fn process_test_file(file: []const u8) !void {
const parsed = try std.json.parseFromSlice(std.json.Value, allocator, file, .{});
defer parsed.deinit();
std.log.err("{}", .{parsed.value.array.items.len});
}
just trying to print length and this gets stuck
no idea why
test "sm83-single-step-tests" {
const io = std.testing.io;
const dir = try std.Io.Dir.cwd().openDir(io, "tests/sm83-ssts/v1", .{});
const allocator = std.testing.allocator;
// load test .json;
// for test in test.json:
// set initial processor state from test;
// set initial ram state from test;
//
// for cycle in test:
// cycle processor
// if we are checking cycle-by-cycle:
// compare our r/w/mrq/address/data pins against the current cycle;
//
// compare final ram state to test and report any errors;
// compare final processor state to test and report any errors;
const helper = struct {
fn process_test_file(file: []const u8) !void {
const parsed = try std.json.parseFromSlice(std.json.Value, allocator, file, .{});
defer parsed.deinit();
std.log.err("{}", .{parsed.value.array.items.len});
}
};
const file_buffer = try allocator.alloc(u8, 500 * 1024); // 500 KiB
defer allocator.free(file_buffer);
for (0..0xff + 1) |ins| {
var path_buf: [32]u8 = undefined;
const file_name = try std.fmt.bufPrint(&path_buf, "{x:02}.json", .{ins});
const file = dir.readFile(io, file_name, file_buffer) catch continue;
try helper.process_test_file(file);
if (ins == 0xCB) {
for (0..0xFF + 1) |sub| {
const sub_file_name = try std.fmt.bufPrint(&path_buf, "{x:02} {x:02}.json", .{ ins, sub });
const sub_file = dir.readFile(io, sub_file_name, file_buffer) catch continue;
try helper.process_test_file(sub_file);
}
}
}
}
can you find any issues?
its inside a test so my logs arent working
maybe change test { to pub fn main(init: std.process.Init) !void { and zig build-exe?
ok nvm its not stuck its just slow af
there's so many minor inconveniences while using zig
those are shrimply the complexities that other languages hide that zig makes you handle in the pursuit of writing better software
I understand the first part, I don't necessarily see how the 2nd part holds
ignoring and abstracting away those complexities is what makes most software un-robust. most of the best software that is robust and stable gets to be that way only after years of manually applying discipline and lessons - of course, zig isn't a silver bullet to achieving this, but it outlines the areas where such discipline will be needed from the outset
there are many things that you can abstract away and never have to worry about them
the most basic example is memory allocation
in basically most languages, memory allocation is basically shuffled under the radar
even in C, the manual memory management language, basically mostly treats it like it can't happen by default
even most of the best C codebases probably started with such an assumption, and had to manually evolve to handle those failures gracefully
who is ultimately zig for?
people that want control over their program
systems programmers, and people who like writing robust software in the same way systems programmers do
People who like C/C++, but don't like C/C++
Wouldn't say that
I want a hobby language to make cool stuff with, I make stuff gaming adjacent so I want performance, ease of use and easy portability across platforms
Not everybody here like or know how to program C or C++
I don't like C/C++ I find them quite primitive
ease of use is kind of relative
I find zig very easy to use
I mean, no apologies needed, it does suck
Heh
compared to rusts docs
we are aware of this
We are aware
And there is not much that can be done with the language moving
I spent so much time manually figuring out how to read a json file
I know but it somewhat represents me hehe, I find Zig a middle point between Go, Rust and C
in another sense, zig is for people who are comfortable with re-inventing the wheel (though without actually obligating you to do so), and reading the source code for what you're using (which it makes much easier to do compared to C/C++ and Rust)
I like the rich type system of Rust, the way you can guarantee so many things at compile time, you never have to even think about it at runtime.
you do have to think about it at runtime
the idea that you don't is wildly incorrect
the static guarantees model a subset of your program's invariants
Rust only promises you that your code takes that shape (In that sense any type system can expres this with more or less pain) and that their memory safety guarantees are correct
The runtime invariants are still for you to handle
nope, allocation failure is almost always covered up, and failures are often swallowed with unwraps and expects
including panics and the sorts
unwraps and expects are an explicit opt in by the programmer that "ok I can crash here idc", and easy to find out when it does
integer overflow is also implicit - and in the likely event zig gains ranged integers, even arithmetic in zig will only be able to fail explicitly, vs in rust where it is implicit
okay, by that logic all failures in zig are exlipict too, so?
zig doesn't make it as easy as rust to do it so
everything is manually imperatively done
Because rust hides more than zig
Zig doesn't believe in automagic
Because rust is less explicit
obviously both are turing complete languages, you can do anything in anything. But the type system of Rust is far far superior by default
Not for raw pointers
fr
Zig excels by far in any endeavour that requires unsafe wrt type systems
I don't think 90% of people who use rust have to deal with raw pointers
for example, I never did, in any of the applications i used
you model the reality of your problem in the way Rust wants it
You can still only model a subset of it
You must still evaluate the actual behavior at runtime
Just like Ada/SPARK
Safety yay
obviously not everything is runnable at comptime
I mean in the sense of what you can model
but you can guarantee how those runtime systems will interact at compile time
Same, I really want to contribute to both docs and the web docs mostly
In order to perfectly model your program in the type system, you would have to write a finite state machine
The one feature I really to see is the one by Mitchell, for multiple or descriptions on doctests
And model your program entirely at comptime
With tne runtime merely being a mirror of it
By this fact, rust does not guarantee any logical correctness, only the specific invariants you care about
I just don't see the pros I gain from using zig, are that worth it compared to the cons of not using rust.
Like I said, different audiences
If I used rust, I would largely be programming in unsafe, which is just not worth it compared to using plain old C
There is this I heard once in my doom scrooling, it goes along the line of:
"The major burden of knowing magic is that there is no longer moment of quiet wonder, or moments of luck, you got to manage it yourself"
well, you forgot one important thing, and thats lack of experience in writing Zig, its easy to judge language you don't properly know
learning zig is ruff
yeah I am just judging it as a newcomer, there's like so much friction trying to learn zig
and indeed not super welcoming, as best src is source code anyway
interacting with anything outside zig with zig is super tough
like io and files and stuff
If we're talking about friction to newcomers, I don't think rust is earning any awards there :P
atleast with rust, the docs are great
Aside that, I like to consider the backing each language had, Rust had a whole Mozilla and team of research, and it's already stable. Zig on the other hand it's purely on Andrews vision and community.
Again, pre-1.0 language. Im certain the rust docs pre-1.0 were nothing to write home about either
also not to mention the zls lsp is not that good, it doesn't show literally most of the errors
Can I ask, what is your point here? Why have you attempted to learn zig when you have near zero enthusiasm for it?
i would refer to tutorials or guides maybe as docs are not really that bad, you can search any function and check what it even does or check how its used in std
You know it's pre-1.0, but seem to expect a 1.0 experience. Seems like misaligned expectations
I want to learn zig, I find rust has a lot of stuff that I like but a lot of features I don't use, and it's a very complex language. I was attracted to Zig because I thought it was a very simple language with a very small set of syntax and features you need to know.
But my learning process has been full of friction and hurdles trying to the even the most basic stuff like reading a file or printing to stdout
Same dilemma I had with all Rust, Go and Zig
The language being simple means you do have to do more work
but i find it not so simple actually, it has so many builtin functions, that increase the language complexity a lot for me
Fewer abstractions between you and the reality of the code means you become the master of orchestrating what other languages would for you
Found the damn quote:
“Look, a shooting star!”
“I’ll pass.”
“What?”
“Wizards can’t wish on shooting stars. Or anything else, really.”
“Why not?”
“That’s how it works. The price for learning magic is that you become responsible for your own magic. No more lucky breaks, no more synchronicity, no more spontaneous moments of quiet wonder – you have to do it all yourself. And in exchange, you get to shoot fireballs out of your hands.”
“... was it worth it?”
“Fuck yes.”
Thanks tumblr
Not really. Compared to other languages, it has relatively few, it's just that most languages hide them. Zig makes them explicit.
Hell, even Rust's format syntax is a builtin, whereas zig's is userland
well first 12h were tuff for me, once you get into it its not that bad tbh, i never used lowe level language before but the concepts in zig are pretty straightforward and simple compared to C++ and even Rust
You can build your own std, and is what big projects usually do (And some crazy people)
There is nothing magical about std
Zig Boost
And a lot of zig builtins are just things that other languages provide on the std in less optimal ways like @abs
As far as im aware you cant even replicate it in user land there
Even those are often secretly intrinsics, with the compiler explicitly recognizing them and replacing them with its secret intrinsic
In modern CPUs these are often single instructions so it makes sense to provide them as first class citizens
You maybe could with proc macros, but that's ass
Well yeah, thats the point of them, they cant be part of the std exactly because of those properties
I think the main issue for me is it's hard to get info on "how do I do X in zig" google is useless for the most part, LLMs too. My only options remain are reading the subpar documentation or ask stuff here and wait for others
Mostly true.
I enjoyed learning zig without google
There is the langref, there is the code
Figure it out
Yeah, that's pre-1.0 lang experience, esp today. We've all been through it, and some learn better with different ways. I just read the langref in a few days and rawdogged it for a week
If you want an LLM to do it, I do recommend you to get a local one read your zig std and ask it to research it
tbh its ruff, but after 12h of zigging you start anyjoying it trust me, maybe start with simplier projects or something
You are looking for the wrong thing, you shouldnt ask "how to do x" you should ask "how does x work" and actually learn the concepts, once you do, zig cleanly maps the ideas out for you to use
Took me 1 month doing a GBA game that I stopped to learn zig
I feel like the rate at which I can learn zig is being limited by a lot of external factors outside my control.
With Rust I learnt it pretty fast, because the linters are great, compiler error messages are state of the art. and LLMs work well for the most part and docs are also pretty good.
I know "Zig isnt 1.0" etc but just stating my experience
I think it will be good for you in general if you keep going. You can learn most of Zig from just the language reference, the stdlib docs and some digging in the code. You'll learn to work with fewer resources which is important
Well, depepends on how you measure. I obviously wasn't an expert after a week
lol, tbf, true
maybe that looks like it but Zig has verbose error messages, i think you just lack the docs you are looking for
Zig's moto "Favor reading code over writing code" very much applies here
I think your method of approaching it is the one stopping yourself 😅
0.7...
But now you just need to read them
and after all that, my BinarySerializer is smaller than same code written in JS 😭
There are still some odd balls out but they will eventually get addressed
A bit before my time I tnink
Yeah, few n far between
yeah I don't have trouble with language features a lot. It's mostly figuring out stuff like stdlib, builtins, and allocators and pointer related stuff
lol, it didn't improve much until a few ver ago
I have my own binary serialization and it cant even map to js properly (language with no enums/unions and no proper integer formats)
builtins are on the langref
Allocators and stuff is a conceptual thing https://zig.guide/standard-library/allocators/
The Zig standard library provides a pattern for allocating memory, which allows
From Zig SHOWTIME #5
https://zig.show
0:00 Title
0:39 Talk
34:19 Interview
Oldie but useful
A damn classic
Zig SHOWTIME is still a rly good source for concept explanation, wish they made more
oh this is good
Not Zig but also helpful(from the creator of Odin) https://www.gingerbill.org/series/memory-allocation-strategies/
Sad his troll ass just comes to meme nowadays xD
Nothing changed <3
Eh, he did his part
I know, I am memeing
stuff like reflection and having to use it for generics is very new to me, I have never used stuff like that in any language
it feels very hacky, seeing a language refer to itself
Ruby does that, and does rust
Well
for metaprogramming
iinstead of Generics
but still
macros kinda have their own language, proc macros work with the token and ast itself so it feels hacky to me somehow
idk its just what I am used to i guess
I do not like macros in rust a lot, proc macros tho are very cool
Ruby reflects on itself and you can create generics that way =3
is indeed just different
since you can basically do very cool stuff like embedded html/SQL etc and validate them at comptime
C++ can have reflections if you really want to
Lets not even start on functional languages too
btw, how can I convert a u16 to a packed struct(u16)?
bitcast
Zig is actually pretty robust at reflecting, it's just that it makes no effort to try to hide any magic from you, so you end up with the "guts" of fancy generic code exposed directly to you. Other languages hide it away
const data: u16 = @bitCast(packed);
Why would you validate sql at comptime? Just create the statements at startup and the db will do that for you, its such a waste of compute to do validation on top of validation on top of validation for no reason at all
And is slow because sql is a weird lang in itself
And if you are mergings strings instead of using statements you are just using sql wrong
oh ok, it can just derive the type too
not only syntax validation but it can do schema validation, so that your complex query actually match up with the table schemas
not to mention that btw, sqlite supports dealing with raw pointers, good luck doing comptime validation on that
A sql table is a schema wtf
Did you by chance come from the js world? That would explain a lot
the main benefit of this is rust-analyzer can just hints and errors while writing code. You don't have to compile or run your sql engine to figure out when some large query you wrote went wrong
That is just asking for problems imo
You know you can just use .sql files and use a sql lsp right?
I am unable to explain it you properly, you should take a look at the sqlx crate in rust
yeah am aware of that package, I wouldn't be comfortable with my tooling running directly to interact with an arbitrary database automatically
You should if possible do:
Bring data -> Transform it into your program domain data -> Deal with your own data -> Send message to something that will insert it or deal with the interaction on your data/db
Not to mention most sql lsps allow for testing queries on the fly with ide shortcuts
but I don't need to, the rust compiler itself can validate my inline sql queries
That's so dirty to me ugh
I guarantee you i can write queries that would fail inline validation yet work perfectly fine in sqlite
whatever, no sense arguing about it. some people like control over their tools, and others like em to run wild
Yea
I mean if you had something like rust-analyzer you understand why its beneficial
I have used it
and also not using an ide
rust analyzer is quite a piece of technology
You dont need it; its a bandaid for bad development practices
I just hate using rust
x2
and think that the things rust does are anathema to what I value
but anything that is a bandaid for bad practices is bad software for me
(Not rust analyzer itself, but things like in-code validation of other languages...)
The biggest downfal of rust was it getting so web-piled and doing all the same mistakes and awful practices those people do
what? an lsp?
Inline validation for external languages that have their own way better tooling (with no impact on compilation speeds either)
Especially given that just because a sql query is valid in mysql doesnt mean its valid in postgress, the rabbit hole of sql is so big there is no concievable way that rust's inline checking actually works properly
I just told you rust gives you the tools powerful enough to do that if you want, you aren't forced to use it?
So powerful they wont even work for real use cases 
Not to mention they will explode the slow compilation speeds to a whole new magnitude for no reason at all
without proc macros, stuff like clap and serde won't be so good at all
Again, i will just stand by this and go use my time on something more productive
I mean is the downfall in the room with us? to me it seems like it's one of the fastest growing languages out there
I think they mean wrt the quality of software
Actually, last take:
Serde is garbage, and any lib that tries to be like it is just worthless. Its literally web mentality at its peek and doesnt fit in a native performance-driven environment
have you used clap?
sure have, and no thanks
Universal serialization for Zig: JSON, Yaml, XML, MessagePack, TOML, CSV and more from a single API. msgpack.org[Zig] - OrlovEvgeny/serde.zig
Still no thanks
I know it may be hard to grasp, but just as much as you seem to think we don't understand your perspective, you seem to lack any grasp of ours
Using text protocols for native development is the stupidest shit ever and i hate how much rust is polluting the space with it
All the "fast text parsers" can be beaten by the most lazy half assed binary protocol implementation
If not zig clap, what do you guys use, if you do
Just do what zig does, StaticStringMap and handle it manually
zig std.cli nowadays, zig-args historically
There's a cli namespace?
Still have to look at zig.cli, heard that the progress stuff is amazing tho
Or is that std.progress?? I forgot (the thing they use for the build system)
std.progress is zig's fancy progress "bar"
I mean I get it, you guys want things to be as barebones and handwritten as C, but then I don't know how Zig is a modern language, apart from having some stuff like Optionals and Err sum types
unrelated to parsing CLI arguments
well is args
args just works so it's easy to handle it
Oh
Yeah, there is no need to, unless you need more than 40 options or some fancy graphical stuff libraries will just tie you to their constrains and be generally slower
I was wondering for external library mostly
we don't want = C, zig approaches the problems tackled by C from first principles. in some cases that means coming to the same conclusions as C, but in many other respects it means reaching novel conclusions and approaches to software design
bun had a lot of issues because they were using clap and couldnt do the features they wanted to (this before the AI slop, like 0.3 days)
Yep, they were in the early days and instantly regretted it, tho honestly that seems to be the entire thing about bun, ship as fast as possible and regret everything later
I mean if Zig is just C+ then idk why people wouldn't just use either C++ or C, with their 40+ years of libraries and knowledge base
C++ sucks
Serde, Clap and a bunch of web-driven libs are well within that mentality
As someone that uses it for fun
I don't really get serde
C is good, but C sucks to write modern software in
That's just it
I see a point in Rust via macros but
C++ sucks, and C is chained by its baggage and committees. that aside, that's literally not even what I said. "tackling the same problems from first principles" does not evaluate to "it is C+"
I don't understand how is it beneficial in Zig
My C nowadays looks like zig, why not use zig?
I work professionally in C++, Zig's toolchain is just a dream to have compared to what the ecosystem in C++ provides
You seem to work in black and white my dear, we are working a bit in the middle
We don't think zig is perfect, but it accomplish exactly what we expect
that's what keeps us comming back
So let me get this straight.
You complain that zig has "to many builtins" yet you are saying that people would prefer to use a language that has 5x the keywords and builtins? Im failing to see the logic here.
Zig is lower level than C while providing some high level abstractions (like defer) that C doesnt, it also doesnt have some pitfalls that C does (goto hell) and is exicit about its intent; thats what makes it modern and a C alternative
And on the topic of libraries; all C & C++ libs can be used seamlessly in zig with a simple translateC step on the build file
yeah, putting all of your criticisms together mostly just makes it sound like "Zig isn't Rust"
I'm sure you don't intend that
but that's pretty much where this is heading
nitpicking, you can't translate C++ libs without a C interface available first
I feel you are a bit forcing yourself into Zig
C sucks to write any complex software in especially if your program is heavy on control flow or matching
Nah, I think you can write complex software, it will just be difficult
Yes but it can be used seamlessly (you dont have to deal with the pain that is compiling C++ libs yourself)
if for every point about zig you just say "but rust does it this way which is good", then you should just use rust. you shouldn't use zig just because it conceptually represents an appealing aesthetic called "simplicity"
oh nice, this thread looks like an average quick question in #zig
I didnt say you cant, i just said it sucks 
eh, I wrote a Zig binding for a C++ ish library, I don't agree with "can be used seamlessly"
my main reason for using zig is just that it's a "simpler" language that lacks a lot of features, which maybe forces me to learn new stuff and implement some stuff from scratch, but I am contemplating why not just learn C at that point
Hmm i think i see what you are talking about, but i cant think of another word to describe my experience with it tbh
go for it
well, learning C would very likely make you appreciate many of the things Zig does :P
you will either love it, or learn why we stay here xD
Or both!
(Or neither but that's the bad end)
my main deterrent for C is the lack of generics and no proper build system (I hate cmake)
I saw you wanted to try to build RE2 with Zig, did you manage it?
Zig forces you to learn how your machine works and what it is actually doing under all the abstraction layers and gives you all the tools to explicitly make it do what you want it to do not what the language secretly wants it to do
their bio is fitting for this decision
that is fair, CMake does suck. although, you could always use build.zig :P
nope, didn't even try, I don't have a usecase for Regex in Zig
but you should try it anyway
Use zig as the build system
lacking generics is a good lesson in the very fundamentals of abstraction
very quickly forces you to learn type erasure and writing agnostic algorithms without a compiler to copy paste your code
Sob, I was gonna ask how did you handle Abseil in that case, thanks still
good, lets keep regex away from zig
There is a zig re2 like lib
bespoke lexers >> regex
agreed
What's wrong with regex now
I should make a post about it
I mean without generics you just write macros I guess, and that's just wild foot shotgun territory atp
Nothing, but bespoke lexers are the better solution
well, not necessarily just macros
there are many avenues of abstraction
you can use vtables and other abstracions
Message Passing
yems
I had a look, decided it's not worth it, moved on and be happy
Ungodly slow, leads to bugs without you even noticing them (cloudflare would know) abd when its present people want to use it for everything
Slot based systems
I'd rather waste a day writting a parser for my use case than touch regex ever again
Command based abstractions
which, to be clear, you can also learn all these things in zig. but if the point is to learn new lessons through restriction, C is certainly the ultimate logical conclusion
Personally, i think Zig is a better teacher than C for low level as you actually learn about managing memory, C just hides it all away in its functions
you certainly seem to be capable of committing yourself to gritting your teeth and learning a language you think isn't that beneficial, so if you're going to do that, I do really suggest trying out C
this is true, but C forces you to be more creative in certain ways to write general purpose code
because it sucks
the main thing I need for a perfect C is rust enums and generics, that would be the perfect language for me. a good build system would be nice to have too
merely the fact that Zig has different pointer types, that's enough to force people to think about pointer better than C
is the goal learning or finding a perfect language?
There is no perfect language, something will always stick out
because earlier you said you were chiefly using zig to learn
There's no perfect language
I am just theory crafting
like perfect for what I am looking for
So me
True, you can still do it, its just a completely different beast new people will never want to touch
Since we are at it, try vale, I do wanna see someone testing it
Crystal
Pointers not being optional by default is a godsent, you can actually feel safe when using them and inherently just treat them like any other type (excluding its potential mutability/side effects concerns that is)
well yeah that too, but ultimately I just wanna write cool software that's easily portable and efficient.
Go
well, zig certainly gets the easily portable and efficient stuff down
learn ATS for bulletproof low level lang 
Ada is another bulletproof one
but is it C level?
yea
Zig is the best for that, it just takes some elbow grease and willingness to learn how computers work and drive them to do what you want without hidden magic or handholding
They have native memory pools that are basically arenas for memory sections @lofty bear
Spark
Also I am doing it mostly as a hobby so it has to be fun and kinda approachable, good tooling and dev ux helps in that
Good tooling will never happen in a pre 1.0 language; zls is pretty much worthless with generic code.
DX is very subjective and depends on how you think about software
Some people think anonymous functions & colosures are good DX, others hate them and their hidden magic properties
ruby apparently is DX dream until you wake up in macro hell
Java 😋
DX that strikes a good balance between convenience but without affecting the end result. For example not languages like javascript that trade dx for everything
how come most graph related tools are written in Java, annoying
what about jai
i like anonymous functions (no captures, no fancy features, just purely inline function definition)
PlantUML
is it even released? i don't look at closed source langs
Its still subjective, for me and i imagine most people that like and use zig daily it has amazing DX and maps perfectly to the way we think.
But for you it might not; just have to accept it and try odin instead
Just purely for inlining i also dont mind them but depending on their implementation they can be hard to debug (im pretty sure this was a point Andrew brought up against implementing them)
nope it's in closed beta
Try odin, maybe a rust piled brain will like it more given it has concepts that resemble traits and also has explicit function overloading
C3
odin and C3 are pretty neat choices too, yeah
both a bit more C-spirited than Zig
but also go a bit further in what you might call "modernization"
ie Odin was mostly developed for gamedev
Neater than Zig tho?
right... but struct {fn f() void{}}.f is fine...
I don't ever write that, I just name the struct
Odin especially with how they do function overloading its actually pretty neat and probanly the best implemenation of that feature i have ever seen and in a way kinda resembles what we can do with comptime
odin does too much, C3 I have never heard of
const helper = struct {
fn foo() void {}
};
helper.foo();
also lets you write multiple of them in there
odin does too much?? bru
Well that is named properly which goes against the definition of anonymous but yeah im on your side there
What? Odin is more barebones than Rust and is far more explicit in its overloading syntax 
no like it has a pretty huge stdlib right
has sdl and everything directly in the stdlib or something
vendored libraries
Thats because it includes things like sdl and raylib on its std but its an std you dont use it all and like any decent language it doesnt compile it all into your program
it's also very game dev domain focused, I don't want that
Odin is to gamming what Go used to be for networking (Go is turning into a shitshow lately)
then time to pull off Andrew's trick and write yet another language
I remember that my entire reasoning to not use this language was as simple as c++ ptsd
I still admire the amount of code quality checks it has (Golang CI stuff), just some, not all of course
it's very C inspired, so obviously you get all the suckiness associated with that, but ey
fn void main
Go's tooling is indeed amazing but the language is starting to go south sadly... the devs are not standing on the principles the language was started on
also like I want the language to be somewhat popular and have a community around it, otherwise I can't really discuss stuff or ask for help
any language you'll hear about has at least some community around it
just depends on the degree to which that is the case
Even OCamll has a community
first time hearing about c3 tbh
ocaml I have heard a lot in the functional programming groups
Scala, Kotlin, Gleam, Elixir
you've heard about it now
I just see gleam the way i see mojo, not a language, just a not so useful extension to another language
Poor 🔥
functional programming must be the anthesis to everything you guys stand for isnt it, the most abstracted abstractions of all abstractions
why doesn't zig work with .z
generally speaking, yes
can appreciate many of its concepts
hell, a lot of modern language features are inspired from FP
but the core values of FP are quite antithetical to zig yes
You should try F#
.z is already a taken extension in unix systems
boooooooo
(the features I refer to are stuff like optionals and error unions, with operators that make them very akin to monads)
yeah like personally I really like functional programming, I think if there weren't any performance detriments, any pure FP language would be my ideal hobby language for building stuff
The only thing i can agree on with the entire FP movement: keep your functions as pure as possible
I mean, I would ask, what's really the harm in the perf cost if what you're designing is primarily hobby stuff?
What was that FP lang with the zig backend called?
Haskell
it makes a lot of the stuff I build have unacceptable performance
like wot
GHC's haskell has relatively low performance overhead, it's just harder to reason about algorithmic complexity + their toolchain is large
if'n you don't mind me asking
like game engines, emulators, etc
fair enough
well, if you want the performance, you do also gotta accept the restrictions that come with it
Write your own dream language 
The entire issue with haskell is that you can override symbols
You can make + mean literally anything globally (its an even worse system than overloading)
I feel this became a
forth is very FP like and low level
#programming-discussion more than understanding Zig now
yea, the language extensions are aids
tbf, in this sense it's akin to lisp, where the symbols are not very strongly tied to the language
Thread closed because too much heat idk me no english
If you want modern FP with decent performance, Elixir is the way (tho its more geared for networking than anything)
philosophical discussion about as to what @compact sage is looking for in zig, and in any programming language in general?
For me personally, zig is that rust--
it get's worse with language extensions, where they can introduce whole new syntax rules. so to read arbitrary haskell code, it is not enough to know the base language, but you need to know about how each language extension affects the code
I mean I still haven't given up on zig, I will finish my current project before ultimately deciding if its for me
but yeah, I will complain here
that's fine, just also expect us to pushback :P
that's fine, most of my complains are due to being unfamiliar with this stuff and seeing it for the first time
imdeed
If you find zig is not for you, try C, and if you also don't like that, I suggest you try zig again and see how you feel after having experienced C!
I mean I already know the annoyances of C, I would rather learn zig than deal with them
knowing C certainly does elucidate many of Zig's decisions
I learnt C as part of my college course, never built anything serious in it
well, I would personally argue that building something a bit more serious (ie requires sitting down and thinking about how to design stuff without necessarily having a direct answer simply available to you) is an important part of fully learning a language
btw, when I do !void as the return type, how does zig know whats the error type
but fair enough
it analyzses the function body, and based on the runtime code, puts all the possible errors together
ie, if it sees you do return error.Foo in one reachable branch, and try foo() on another function with an error set error{Bar,Baz}, the IES would be error{Foo,Bar,Baz}
oh it creates an error type on the fly
"on the fly"-ish
it is lazy about it
in reality it doesn't resolve the error set until it has to
ie, it can't do it when you declare the function
errors can be any type or have to part of an error enum?
and only works with functions with bodies (so no over function pointers)
it does it when you query info about the function type, and when you call it
errors gotta be an error set, yeah
errors are a single big set with all possible errors
so I can't just return a number let's say? as an error?
Error types are just subsets of said set
non
For that we have diagnostic pattern
just to clarify something: errors and error unions are not like rust's result type
One of the most commonly cited weaknesses of Zig is that error unions don’t have an error payload, unlike Rust’s Result. So, human-readable error information is usually either passed around with a Diagnostics struct, shat to stdout, or not there at all. Here’s an example of a Diagnostics from std.json: /// To enable diagnostics, declare...
they are more like a tool for controlling control flow, and distinguishing between the happy path and error path
so like in rust the result type can have anything as an error right, is that not possible in zig
I think it marks error paths as cold too iirc
non, like I said, they don't serve the same purpose
See related
I thought this too for a long time, and was corrected by mlugg
I swear it must have been the case a long time ago in an older version
Then I remember mlugg saying it didn't V=
oh that's pretty complex
and retro-actively said, it doesn't matter
Simpler than it seems
There is always a first time, and if you aint ready its gonna be painful to take it in
if you have more complex data to handle, or something where "this is an 'error' case with more data to deliver and handle", then it shouldn't be thrown away into the error path, and you should handle it like a normal path. or, if it really is just part of the error path, then using the diagnostics pattern is the most appropriate thing to do
I need to do this for my markdown parser...
dewit
is a pattern for "Need to push something as an error outside with extra data"
can't the error enum be a tagged union and contain payload data that way?
that means you have used all your correct resources and need explicit user handling
non
it doesn't make sense with the way zig has designed them
if it was like that, you couldn't simply try errors
since all errors would need to be distinct and closed sets
Can always use an out pointer to get some more information
Still got some features to implement gonna do it afterwards (the entire goal is to no stop parsing on errors so diagnostics will need some thinking)
but the way they actually work is that all error sets are just subsets of the global error set (which can be referred to as anyerror)
it's very akin to the C approach to error codes
in fact that's kinda what andrew modelled it based off
No, errors are meant for control flow not to pass data around
also how do you guys debug your zig programs
gdb, lldb, and prints
printf debugging most of the time, every now and then lldb
depends on the kind of bug
There's an lldb fork that adds better zig support iirc
can't beat the classics
Pain & suffering when dealing with wasm
lldb and gdb which one is better? idk how to use both
I never had much problem debugging wasm
Debugging wasm panics on the web is actual hell on earth
std.debug.print + lotta tests helps
both are around the same ballpark
lldb can be better with zig thanks to llvm
I had a rant about this yesterday in #gui-dev
lldb has usually worked better for me
gdb is always kinda finnicky
wait but didn't zig ditch llvm or something
no
Kindof
Can say lldb works rly well, just can be a pain to setup in some environments
The true answer is NO
it's divorcing it from the codebase, but zig will always support emitting LLVM
The General bit is that the codebase is not dependent of llvm to generate code
But LLVM is the faster backend
I love andrew but man, we are going to always have to deal with that damn misconception for years onwards
And even then, debug symbols are quite different from emitting full llvm ir
I hate andrew, but man....
Kidding
Depends in what sense you mean :p
not compiling ofc
When zig actualy puts nice Dwarf itself, debugging with gdb or lldb will be a as good as the tooling around zig for those tools
Sadly using llvm seems to be the only way to get crypto/hashing code to not slow to a crawl...
yeah
Oh and tracy your stuff, adding metadata into your program helps
and for the graphical side, renderdoc
we are forced to use llvm at work still
can't use self-hosted at all still
hopefully someday
for logging, is std.log enough or do you guys use third party solutions
( I consider performance as a kind of bug so tracy is technically debugging)
The same code that takes less than 500ms to run using llvm can take more than 6 seconds with the zig backend... (for crypto that is) its quite the pain rn
I write my own if I need more
it's usually enough yeah
std.log is very customizable, never felt the need to use something else tbh
Only had to for multiple sinks
we use a custom logger at work, but that's for a bunch of reasons, like wanting more log level granularity, and wanting to pass around a logger interface thingie that can make scopes required, and also because we do a bunch of IPC stuff that it simplifies the data flow of
since multiple sinks is kinda bad in current std.log api
never use third party libs for logging in a library when using zig.
If you are the final point of the code you can use a lib but ofc one that provides overrides for std.log is ideal
How do you configure the log levels while running the problem? is there a env variable
you override the logfn to something that respects a runtime log level
is there no cli flag
no
never print in a library
Make a event pump for errors =P
You can make your own
yeah i am writing a binary project
where would it even get the CLI flag from
you are the one writing the main entry point
I am not sane, but I am quite the perfectionist
no like rust has RUST_LOG env variable that a lot of logging crates respect
Thats not a flag
yeah naur, we don't be doin that in zig
and yeah, that not a flag, that's called an env var
yeah
I presume they also want -q and -v
Environment variables and flags are 2 different concepts and serve different purposes.
But zig doesnt generally introduce things on a whim, you can do it with your own abstraction but there is no reason to when you can make use of comptime and proper build flags for this
having a general env variable that your program currently have is a choice... (for libraries)
I don't like personally exposing envs that could be an API that the user can use to implement said logic
This is the Way
I dont like env vars at all, build time flags are superior and more controlable
almost at 2k messages, i wonder how much that would cost in LLM tokens
Sometimes needed
Like, you want to make a choice between wayland and X11 or Arcan in Linux
(Curse you @lofty bear )
Yeah its a necessary evil for things like terminal color detection but especially if you look at the web people you will see them passing sensitive credentials via env vars and its just awful
you also don't want them on the binary, you want to pass them via a secure network at startup
Or a configuration file that gets loaded on startup
truly
human token-maxing out here
That's still an env var in my eyes
and way too insecure
But I use both so 
yeah, implicit config files only make sense as an explicit choice for an application, not a language's runtime
(and should be overridable)
Eeh, not really, there have been some vulneravilities caused by exploiting env vars before; with a file they would have been avoided
that's why I said I would prefer using cloud secrets api, at least there I can blame google ;3
Well, env vars can be used to escalate permissions, a file cannot if its put in a protected place
But actually, how would you guys load secrets if not via a configuration file?
to be clear, I do agree config file is generally better than putting everything in env vars
just don't think it's a silver bullet
I know its not, which is why im curious to what other ways people recommend doing it
the most powerful tool for avoiding the vulnerability in the first place is having keen awareness of those liabilities
I recommend doing it with intention, proper documentation, and clear policy :P
and always as a decision of the end-programmer, before it's delivered to the end-user, never as an implicit part of a toolchain or tech stack
Using runtime api to read the configs that uses proper secret communication
A file that you have to open with a key
A config file
Env vars
that's my order
I use the second one in my own personal hobby server
Can you explain what you mean with a file that you need a key to open? (Especially on an environment that cannot make requests to another server)
should probably carry on discussion elsewhere
Good idea
Already did
Ping me in #programming-discussion 
155 messages dear god let this thread become a new channel already
It's always good to know C
😭
Odin is actually meant to be general purpose, it's just that it ships with a bunch of libraries useful for gamedev. But gamedev is a very wide domain of programming(consisting of graphics, UI, systems, networking etc etc) so "a language for gamedev" isn't really possible without being general purpose
Bill is working on adding an http implementation to core
we may also see an sqlite module(though he doesn't plan on adding other databases since that would imply creating some kind of cross database abstraction)
It's not "domain focused" but it is quite focused on "feel" and pragmatics. Odin feels very nice to use and fits very nicely in my hand
Odin does generally try to nudge you towards a "data-oriented" style though(with stuff like ZII, soa, using etc). But data-oriented design isn't really specific to games either, It's basically the way to write really fast code(see Andrew Kelly's talk on applying data-oriented design in the Zig compiler)
I also personally think it looks beautiful compared to Zig(which is quite verbose)
Comments about Odin "being for gamedev" always make me laugh. When you actually look at the language its just a general purpose language. It's got a bunch of types and math built in that happen to be good for game dev, but that's more coincidence than anything. Bill has said he included it because it's the main stuff people want function overloading for. So just build it in.
The math stuff is actually a really genius idea. The only reason to ever have operator overloading is to support math types, so why not just add the math types?
You just solve the problem you have instead of creating more problems
Yeah I really like how pragmatic the language is. Frankly I would probably lean toward it over Zig except for two things. I really like having access to structs + functions, and Zigs C interopt is just so nice. I don't have to write any wrapper code, just a couple lines in the build system and import like normal.
do you mean like methods?
Yes and no. Zigs solutions of them just being functions that use the dot operator I really like. So Vec2.add(v1,v2) and v1.add(v2) are equivalent
zig is going to have more math types in the future so...
also .add(v1, v2) if you infer the type
so nesting is really clean
That being said, in the case of vectors, I do really wish I could just type v1 + v2. Composing a bunch of functions together gets friggin' old. And I'm only doing simple 2d stuff.
we will get them soon™
"soon"
methods do generally encourage a bit of over-organization imo. Should it be weapon.attack(monster) or monster.hitby(weapon)? Odin is opinionated on discouraging you from over-organizing your code. You just have perform_attack(monster, weapon) without it "belonging" to either type
Karl Zylinski's Tom's Namespaces: An Odin Fanfic is an excellent exploration of the namespacing problem in imperative programming languages such as Odin I highly recommend reading that article before reading this one!. After sharing it on many comment forum sites, I've concluded there's no real "solution." AssociationThe article uses a simple...
in zig it doesnt have to belong either, you just have the option to do it
Zig is ... not opinionated enough?
It's definitely not as opinionated as Odin lol
I know, I'm aware of the arguments, and I'm not complaining Odin made the choice it did, but for me the upsides outweigh the downsides
It's kinda interesting that Odin doesn't have a standard formatter given the design
only odin strip-semicolon
I don't know if it's less opinionated. Just different opinions
hmm that's also it. "Reusability" is what characterizes Zig for me compared to other low-level languages
Not sure I follow. Zig focuses more on reusability?
I'd have to write more Odin to know, but I think I always end up thinking along these lines to some degree anyway. I'd just be explicitly passing it as the first parameter all the time lol