I am learning Zig by looking at examples and documentation. In some examples, the initialization of the generic allocator (usually named gpa
) being followed by defer gpa.deinit()
. Removing this will not trigger a compiler error. So, my question is, how do I know when I call a function that returns an object if that allocates memory - and hence I should free that memory myself?
Do I need to look at the source code?
Most code seems to follow this convention:
- If you
init*()
something, you shoulddeinit()
after you are done using it. - If you
alloc()
something, you shouldfree()
it. - If you
create()
something, you shoulddestroy()
it.
I think the GPA is one of the few things that doesn’t quite follow the rules here, but generally if you create an allocator, then you should also clean it up later.
Removing this will not trigger a compiler error.
The reason why it doesn’t trigger a compiler error is because it often enough isn’t a bug. With an ArenaAllocator for example, we don’t need to free/destroy/deinit data created by it.
However the GeneralPurposeAllocator will actually tell you about memory leaks. So if you e.g. call gpa.allocator().alloc(...)
and forget to free it, then the GPA will give you a stacktrace of the memory leak at runtime (in debug mode). However note that the leak checking is only executed inside the gpa.deinit method, so if you forget that, then you are in trouble.
Hello @antonioastorino
Welcome to ziggit
If there is a deinit
you must call it. Currently it depends on the documentation to specify who owns what.
See:
I understand. Thank you for the prompt and clear answer.
If there is a
deinit
you must call it.
So then, wouldn’t it be an idea to hilight these functions in a “Clean Up” section in the documentation.
So for instance the net.address.listen
function returns a Server struct
, on which you must call deinit
.
Shouldn’t there be a section on the Server struct for cleanup functions,where this is listed?
Further to this, the language server could pick up that this struct has cleanup functions now, and for example, change the color of the variable assigned to another shade, alerting the user to the fact that such functions should exist for this variable?
Just ideas
Zig ameliorates the (real) problem you’re referring to, by the ironclad convention that any code which allocates receives an Allocator
with which to do so.
If you don’t pass something an Allocator
, it can’t allocate, and if you’re being smart about it, if you do pass an Allocator
, then (Chekhov’s Gun principle) it will allocate, and therefore you will need to clean it up at some point.
I try to be rigorous about documenting the memory policy of every data structure I create, and encourage that generally. I agree that documentation and tooling could help with getting all of these things correct, but ultimately Zig leaves memory management policy to the designer of the program, and that comes with certain responsibilities.
Oh, I don’t think we disagree there, just thinking of ways we can get the expose these details nicely in the docs/tools, such as in the example above, where currently it isn’t shown. (Eg other resources such as file handles, not memory always)
Convention is great, but lets try give them all the info we can at their disposal.
So, on the broad premise that the standard library would benefit from more explicit and fleshed-out documentation, I do agree.
That said, anything which has a deinit
function guarantees that you must call it to dispose of the data structure, or your program will be in an invalid state. I would say that explaining that in every case with a distinct “Cleanup” section would be redundant.
It would be good to document the meaning of certain standard library conventions, perhaps as part of the official documentation, perhaps not. I even started something like that, which has gone on the pile of unfinished work, and will stay there indefinitely (I’m good, but not great, at “getting around to it”).
But let’s note that Server
has no doc string at all. It’s safe to say that’s substandard and needs to be fixed across the standard library at some point. When I write a data structure which needs deinit
or destroy
to be used, I do document that in the docstring, every time. That way I, or other users, can easily learn that within the editor, in the heat of the moment.
The PR process is somewhat bottlenecked (not surprising, and very much not a criticism on my part) but I’ve seen documentation PRs land before. So that’s something you can assist with if you feel the urge, just plan on showing some patience with the process.