5 ms·
std.mem.Allocator is the allocator interface. For that struct to be considered an interface, it must not contain directly any specific concrete implementation a
by kristoff_it 2y ago
std.mem.Allocator is the allocator interface. For that struct to be considered an interface, it must not contain directly any specific concrete implementation as it needs to be "bound" to different implementations (GenealPurposeAllocator, ArenaAllocator, ...), which is done via pointers. An allocator implementation holds state and implements alloc, free and resize for its specific internal mechanisms, and then pointers to all these things are set into an instance of std.mem.Allocator when you call the `.allocator()` function on an instance of an allocator.
https://ziglang.org/documentation/master/std/#std.mem.Allocator https://ziglang.org/documentation/master/std/#std.mem.Alloca...
File and Socket both offer a `.writer()` function to create a writer interface bound to a specific concrete "writeable stream".
BufferedWriter has both extra state (the buffer) and extra functions (flush) that must be part of a concrete implementation separate from the writer interface.
> The answer is probably buried somewhere in forums history
That's just how computers work, languages that don't expose these details do the same exact thing, they just hide it from you.
- Aeolun 2y agoI think most languages hide this by default because 99% of people don’t have to deal with it.
- metaltyphoon 2y agoYep. For some reason they want to make it boilerplate to write interfaces. Its so pointless
- kristoff_it 2y agoThere are a few different ways of implementing interfaces, each with different tradeoffs, so it's not boilerplate :^)
- 0x696C6961 2y agoI'm only familiar with the vtable approach, what other ways are there?
- throwawaymaths 2y agofieldparentptr. The zig allocators used to use this because it enabled allocator interfaces without type erasure, but it was found to have a minor but real performance penalty as it is impossible for any compiler to optimize for this in scenarios that are useful for allocators. Other interfaces might actually have the opposite performance preference
- cgh 2y agoIf you control every implementation (ie you aren’t writing a library where others will implement your interfaces), then tagged unions are a simple way to accomplish this. See the bottom of this page: https://www.openmymind.net/Zig-Interfaces/ https://www.openmymind.net/Zig-Interfaces/
- kcbanner 2y agoIt's not pointless, because you get to select exactly the design pattern that is best for the situation. Other languages may decide this for you.
- Measter 2y agoMost languages handle this by having interfaces/traits/etc. Zig doesn't have those, so you're forced to use these ad-hoc struct instances.
- Symmetry 2y ago99% of people are either better off using a language with a garbage collector or need to avoid heap allocations altogether.
- kunley 2y agoMy questions and remarks had nothing to do with the way how you allocate memory in programs.
- kunley 2y agoHey Mr Loris. Well, your explanation doesn't really tell why do I call .deinit() on a structure before alllocator() call and calling all the rest important stuff on a structure after such call. I think you guys, while doing great job by the way, are kind of stuck in a thinking from inside language creators' perspective. From outside, certain things look so weird. I need also to be a picky about "that's just how computers work" phrase, you know uttering such a phrase has always a danger of bumping into someone who wrote assembly before you were even born and hearing this makes a good laugh..
- LoganDark 2y agoI don't know about the deinit thing, but I think this allocator/writer stuff has nothing to do with "inside language creators' perspective". To me, even though it wouldn't be my first guess as someone who's never used Zig, it does make sense to me that it's done this way since apparently Zig does not really have interfaces or traits of any kind for structs to just have. In fact when Googling about Zig interfaces I found another post from the same blog: https://www.openmymind.net/Zig-Interfaces/ https://www.openmymind.net/Zig-Interfaces/ which says that an interface is essentially just a struct that contains pointers to methods. In other words when you call the .thing() method on your SpecificThing, that method is producing a Thing that knows how to operate on the SpecificThing, because functions that accept Things don't know about SpecificThings. You can't manufacture that Thing without first having a SpecificThing, and a SpecificThing can't be directly used as that Thing because it's not. There's essentially no other way to do this in Zig.
- chotmat 2y ago> why do I call .deinit() on a structure before alllocator() call This is explained right in the documentation about arena allocator. Arena allocator deallocate everything at once when it goes out of scope (with defer deinit()). You need to call .allocator() to get an Allocator struct because it's a pattern in Zig to swap out the allocator. And with this, other code can call alloc and free with out caring about the implementation. This is just how arena allocator works and not related to Zig's design. You may take issue with how Zig doesn't have built-in interface and having to resort to this implementation struct returning the interface struct pattern, but I think the GP clearly explained the Why.