fn callFunction(func: anytype, args: anytype) @typeInfo(@TypeOf(func)).@"fn".return_type.? {
Nah it should be:
fn callFunction(func: anytype, args: std.meta.ArgsTuple(@TypeOf(func))) @TypeOf(@call(.auto, func, args)) {
fn callFunction(func: anytype, args: anytype) @typeInfo(@TypeOf(func)).@"fn".return_type.? {
Nah it should be:
fn callFunction(func: anytype, args: std.meta.ArgsTuple(@TypeOf(func))) @TypeOf(@call(.auto, func, args)) {
It also does not have to be personal project. Some applications are fast enough with using append and doing a lot of (re)allocating memory in 60fps. Not everything has to be optimised to the maximum. Some stuff just needs to be simple both for maintainability and readability.
For example, if you introduce error handling everywhere, you are gonna have hard time refactoring it. If you preallocate everything and never use append, how much time are you actually optimizing? You have to profile it first, see the bottlenecks and act upon it. Even then your application may not even need optimizing. If its doing what it needs to in a reasonable amount of time, why even bother?
There are so many ways to optimize code and handle errors, so why remove very convinient utilities? Improving by omission can be good sometimes but it has to come from real world examples and very careful thinking. I donât believe that removing append, .? or catching errors with if will make your code drastically better in those aspects.
I think the case of âpreallocate everything and never use appendâ is more about avoiding memory leaks or uninitialized data than it is performance. See Reserve First
For what itâs worth, I love this while loop iterator. Not really any great reasons for that, but I donât find it harmful or deserving of removal like some of the other suggestions here.
I see the point, and agree with the pitfall there. I still think removing a common case function like append is too much. Ghostty example was way too obvious (at least to me) and the other one feels like nitpicking to me. Making the good path easy to code is a nice principle but why remove append instead of just renaming it? Even that feels like too much of a defensive way of doing things because it is obvious that this may reallocate. If append did not exists in the way it did, people would just write their own. I know I would. But thats just me.
Tbh idk if removal would be what, maybe a rethink, but that capitalizing a file considers that is creating a struct with that same name it seems a little weird to me, specially for how it plays with comptime generics over that struct, as its (imo) very common and makes the capitalizatoon not appliable.
In a lesser degree, i donât quite like the return struct { code + functions }; but I get why is nice about it.
I love that structs get âmethodsâ if you pass *@This()/Self, but at the end of the day i donât enjoy that the capitalization of a file says something about the code within it, or that you can declare an struct without writing struct anywhere in the file ![]()
I feel like âcomplexityâ is the wrong word here, because as you said both approach are very similar.
But good library design guides you toward better code. Like âHuffmann encoding of good codeâ. The code you want to favorize should be easy to write and read.
Thatâs why ArrayListUnmanaged became ArrayList.
Similarly append should be the no allocation version and the allocating one should be appendAllocate
That is not that bad of an idea. But my point was that we shouldnât replace single function with 2.
What I think would be nice is to rename current append to appenUnsafe and new append, to be the combination of ensureCapasity and appendAssumeCapasity, cause I think the easiest option should be the safest one.
Just so weâre all on the same page, current append is effectively a combination of ensureTotalCapacity and appendAssumeCapacity:
append is:
pub fn append(self: *Self, gpa: Allocator, item: T) Allocator.Error!void {
const new_item_ptr = try self.addOne(gpa);
new_item_ptr.* = item;
}
and addOne is:
pub fn addOne(self: *Self, gpa: Allocator) Allocator.Error!*T {
// This can never overflow because `self.items` can never occupy the whole address space
const newlen = self.items.len + 1;
try self.ensureTotalCapacity(gpa, newlen);
return self.addOneAssumeCapacity();
}
(for context, appendAssumeCapacity is self.addOneAssumeCapacity().* = item;)
If you actually had a valid point you would just make it using technical arguments rather than using such emotionally loaded language. Youâre so frustrated by not being able to articulate anything substantial that youâre even taking a pot shot at people with Aspergerâs for no reason. Grow up and learn to communicate like an adult.
I like the fact unused/unmutated variables are compile errors. If unintentional it is highly likely that it is a bug.
If intentional the intent can be made clear with _ = unused_thing. I also like that it is so easy to actually show this intent.
If it is considered to remove these errors I would like it to be optional.
I like the strictness.
Go to another language if you donât like it.
To answer to the thread question: I have no internal wish to remove anything from what Zig offers right now.
append should not be removed ![]()
Donât do this, we canât tell people to switch to another language every time we disagree with them, let people have their opinions, otherwise we canât have productive discussions about improvements.
There are many things I only partially agree with in this topic (and it doesnât matter because I might be wrong or change my opinion), it is valuable to see what peoples concerns are and nobody here has the definite answer to how all things should be. It is fine for you to share your opinion, but do it without telling others you disagree with to leave.
Yeah true. Sorry I was a bit triggered late in the evening. Excuses to chung.
Thank you, I was gona read implementation in the morning after sleep, but this just shows that item canât be left in invalid state if append fails. Good to know
The way I see the âappendâ problem is when you append things in loops, and the validity of the whole operation depends on the success of all append operations, without allocating upfront youâll end up with invalid and unrecoverable data anyway if a single append fails. So it depends on whether you want to handle out of memory errors: if the plan is to just crash, you donât need to care much about it, and it becomes (if anything) a performance concern (allocating once is better than N micro allocations).
For what itâs worth, I generally use a helper like this
pub inline fn aac(comptime T: type, list: *ArrayList(T), item: T) void {
list.appendAssumeCapacity(item);
}
then call it like this
al.aac(u8, &content, '\n');
which is short and has the benefit that you also see the type of what youâre appending.
No offense, but how long have you been writing Zig? I find that people that have used it for an extended amount of time just kind of forget that the compiler even enforces this, as you just tend to not make this mistake. And the times it does actually error, I am quite thankful as it finds an obvious bug/smell that has been introduced due to a refactor for example. Also donât think that the compiler should make any assumptions about the authors setup, it sounds silly but why would the compiler assume the author has syntax highlighting or a linter? If it is something that can be caught at compile time, it should, IMO.
In most other languages, unused variables result in a compiler warning and you can configure it in a way to treat this warning as an error. Thatâs what I would prefer. The error is in fact very annoying during debugging, breaking the âflowâ.
Regarding append, I think it is crucial to keep it. Some people seem to think that each call causes allocation. Thatâs not true, pls read the code. The implementation uses a reasonable trade-off between performance and memory overhead.
In most other languages, unused variables result in a compiler warning and you can configure it in a way to treat this warning as an error.
I suppose I just fundamentally disagree with this solution, it is taking no stance on the matter, and just kicking the decision down to the user. I would prefer no warning at all over this ![]()
I just read the code:
append calls addOne(gpa) calls ensureTotalCapacity with newlen += 1 calls ensureTotalCapacityPrecise with an additional length of a cachline or one element, whichever is larger.
edit: Link: Zig Documentation
The ideal reallocation strategy would be to grow the array by a factor of the golden ratio. But for some systems memory is so limited that this would quickly run into OOM.
Still, I like append. And if possible I preallocate the capacity I need. But sometimes I donât know how much i will need.