4 ms·
Having used Zig a bit, and C++ for years, I personally believe that this sort of scenario is pretty rare. You can always add a no-op dealloc method to your type
by isaachier 8y ago
Having used Zig a bit, and C++ for years, I personally believe that this sort of scenario is pretty rare. You can always add a no-op dealloc method to your type if you want a generic function to handle it correctly. Moreover, Zig has awesome metaprogramming support so you can probably think of a way to check the type parameter for a dealloc method and only generate the dealloc call for those types.
- edflsafoiewq 8y agoPretty rare? What about ArrayList? I want ArrayList(T) to work whether T = i32 or T = ArrayList(i32). > You can always add a no-op dealloc method to your type if you want a generic function to handle it correctly You're looking at it from the perspective of the generic consumer, not the generic author. The generic author generally does not have the ability to edit the type. Plus there are many types that do not have methods at all (integers, points, etc.). > Moreover, Zig has awesome metaprogramming support so you can probably think of a way to check the type parameter for a dealloc method and only generate the dealloc call for those types. Yes, you can detect and call a method called deinit that has the appropriate signature using @reflect. You can even put this in a function and call it a pseudo-destructor. But there's no guarantee that deinit has the actual semantics of a destructor without some kind of language-level agreement between class authors.
- AndyKelley 8y ago> I want ArrayList(T) to work whether T = i32 or T = ArrayList(i32). This will work fine. Do you have a more specific example?
- edflsafoiewq 8y agoWhen I call deinit on an ArrayList(ArrayList(i32)) do the elements have deinit called on them?
- AndyKelley 8y agoHow are you expecting to call deinit on an i32?
- edflsafoiewq 8y agoNot sure what you mean. The elements of an ArrayList(ArrayList(i32)) are ArrayList(i32)s. To answer my question, no, they're not deinited. All deinit does is call self.allocator.free on the slice of elements, and for many allocators that's a nop. In fact none of ArrayLists methods take any kind of ownership of its elements. If you shrink an ArrayList(ArrayList(i32)) by one you leak the last ArrayList(i32). None of the methods call destructors on the elements because there is no generic notion of a destructor, only particular ad-hoc ones like deinit methods. ArrayList appears to solve the problem I mentioned above about generics not knowing if a generic type needs to have a destructor called by only supporting types that don't. In C++, you'd write the function that clears a vector something like void clear() { for (auto p = ptr; p != ptr + len; ++p) { p->~T(); } len = 0; } For a vector<vector<int>> the syntax p->~T() calls the destructor on a vector<int> element. While for an vector<int> the syntax p->~T() is a pseudo-destructor call, ie. it does nothing. This makes the same generic code work when the elements of a vector need to have a destructor called and when they don't.
- AnIdiotOnTheNet 8y ago> Not sure what you mean. The elements of an ArrayList(ArrayList(i32)) are ArrayList(i32)s. I believe what he was wasking was "given an ArrayList(i32), how would you expect it to call deinit on the member i32s?". The answer, of course, is that you don't, which is also true of ArrayList(ArrayList(i32)). ArrayList absolutely supports heap-allocated types, you just have to free them yourself before calling deinit() on the ArrayList itself.
- edflsafoiewq 8y agoWhich only brings us again (putting aside what virtue recommends that design over having destructors) to the same original question. If I have an ArrayList(T) for a generic type T, how do I know if the elements need to be freed before I deinit and how do I do that if they do?
- isaachier 8y agoOK I see your point. Worst case scenario, you force the caller to pass a destructor as an additional argument (e.g. ArrayList(T, fn(*T))) C++11 allows for this in the smart pointer classes.
- kibwen 8y ago> Zig has awesome metaprogramming support I'm not sure how to reconcile this statement with the following claim from the linked article: "Zig has no macros and no metaprogramming yet still is powerful enough to express complex programs in a clear, non-repetitive way." Not only does it claim that Zig has no metaprogramming, it suggests that it sees metaprogramming as undesirable.
- mborch 8y agoWhat it does have is compile-time parameters where you can bind a type variable during compilation: fn max(comptime T: type, a: T, b: T) T { if (T == bool) { return a or b; } else if (a > b) { return a; } else { return b; } } The type variable isn't used at run-time.