# Which arguments for deinit (or other invalidating functions)?

**URL:** <https://ziggit.dev/t/which-arguments-for-deinit-or-other-invalidating-functions/10782>\
**Category:** Explain\
**Tags:** language, standard-library\
**Created:** [July 2, 2025, 5:40am UTC](https://ziggit.dev/t/which-arguments-for-deinit-or-other-invalidating-functions/10782 "2025-07-02T05:40:50Z")\
**Posts on this page:** 1\
**Showing post:** 8

<div class="post-metadata">

**Author:** ![jbe](https://ziggit.dev/letter_avatar_proxy/v4/letter/j/f6c823/32.png) [@jbe](https://ziggit.dev/u/jbe)\
**Post date:** [July 3, 2025, 7:03pm UTC](https://ziggit.dev/t/which-arguments-for-deinit-or-other-invalidating-functions/10782/8 "2025-07-03T19:03:49Z")

</div>

> [@mnemnion](#):
>
> If `deinit` needs to mutate, then you have to use that type as `var`.

Technically yes. But there’s something special about `deinit` (and other invalidating functions), which is that the function “consumes” the value (semantically, not technically). Speaking of “contracts”, that means that the function’s argument (or the pointed-by value, in case of `self: *T`) shouldn’t be touched anymore when `deinit` is called.

So nothing stops us from doing something like this:

```zig
const std = @import("std");

const T = struct {
    some_state: i32 = 5,
    // Let's imagine there are many more fields in addition
    // to `some_state`.
    pub fn deinit(self: T) void {
        // Let's imagine we must mutate `self`.
        // We can do that simply by copying it (not efficient,
        // but works).
        var this = self;
        while (this.some_state > 0) {
            this.some_state -= 1;
        }
    }
};

pub fn main() void {
    const x = T{};
    x.deinit();
}

```

My point is that disregarding whether we declare `deinit` to take a `self: T` or `self: *T`, we can always get our work done. But one of those is (in practice) more efficient than the other.

Now my argument in the [second post of this thread](https://ziggit.dev/t/which-arguments-for-deinit-or-other-invalidating-functions/10782/2) is that if we _always_ use `noalias self: *T`, we can always achieve maximum efficiency while simply not _needing_ to expose the implementation’s needs/internals to the caller.

“Forcing” the programmer to use `var` instead `const` (consistently) and allowing `self.* = undefined;` (consistently) is just a bonus. Of course you may argue if each of those bonuses is a pro or con. In my opinion they are advantages, but some people might consider requiring `var` consistently being a disadvantage (and prefer to be able to use `const` at least in some cases, even if that’s just due to implementation details of `ArrayList.deinit`, `Thread.detach`, etc).

Note that [`std.StringHashMap.deinit`](https://ziglang.org/documentation/0.14.1/std/#std.hash_map.HashMap.deinit) currently does _not_ allow the programmer to use `const`, even if it _could_ by following the scheme I demonstrated in the code above.

Now I propose consistency by always forbidding it, while [#6322](https://github.com/ziglang/zig/issues/6322) proposed consistency by always allowing it. And, if I get you right, you propose that whether it’s allowed or not should depend on the `deinit` implementation (which I don’t think is a good idea, but maybe there are arguments that I overlook).

> [@mnemnion](#):
>
> But `deinit` doesn’t mean that the type is mutable.

I don’t think types (structs) are mutable/immutable, but bindings (and maybe pointers) are?

---

_[View the full topic](https://ziggit.dev/t/which-arguments-for-deinit-or-other-invalidating-functions/10782)._
