# Is it possible to apply const on return result adaptively?

**URL:** <https://ziggit.dev/t/is-it-possible-to-apply-const-on-return-result-adaptively/3008>\
**Category:** Help\
**Tags:** language\
**Created:** [January 26, 2024, 8:45am UTC](https://ziggit.dev/t/is-it-possible-to-apply-const-on-return-result-adaptively/3008 "2024-01-26T08:45:40Z")\
**Posts on this page:** 1\
**Showing post:** 11

<div class="post-metadata">

**Author:** ![AndrewCodeDev](https://ziggit.dev/letter_avatar_proxy/v4/letter/a/278dde/32.png) [@AndrewCodeDev](https://ziggit.dev/u/AndrewCodeDev)\
**Post date:** [January 29, 2024, 12:42am UTC](https://ziggit.dev/t/is-it-possible-to-apply-const-on-return-result-adaptively/3008/11 "2024-01-29T00:42:06Z")

</div>

@zigo, I decided to dig into this more to see what options are actually available on member functions. Turns out, it is possible, it’s just _extremely_ under documented.

Here’s the key to the whole thing:

```zig
pub fn foo(self: anytype) ...

```

Self can be `anytype`. That means you can actually deduce self inside the call itself. Here’s an example:

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

const Foo = struct {
    value: usize = 0,
    pub fn getPtr(self: anytype) @TypeOf(&self.value) {
        std.debug.print (// check that we're getting a pointer
            "Self Type: {s}\n", .{ @typeName(@TypeOf(self)) }
        );
        return &self.value;
    }
};

pub fn main() !void {
    var bar = Foo{ .value = 42 };  
    const baz = Foo{ .value = 42 };  

    const bar_ptr = bar.getPtr();
    const baz_ptr = baz.getPtr();

    std.debug.print("Ptr Types: ({s}, {s})\n", .{
         @typeName(@TypeOf(bar_ptr)),
         @typeName(@TypeOf(baz_ptr)),
    });
}

```

Here’s what that prints:

```Text
Self Type: *main.Foo
Self Type: *const main.Foo
Ptr Types: (*usize, *const usize)

```

We can see that `bar` is non-const because it’s declared with `var` while `baz` is the opposite. Even in an example like the following:

```zig
    pub fn getPtr(self: anytype) *const usize {
        // check that we're getting a pointer
        std.debug.print(
            "Self Type: {s}\n", .{ @typeName(@TypeOf(self)) }
        );
        // return a value that has nothing to do with self
        const value: usize = 10;
        return &value;
    }

```

The `self` type is still being deduced as a pointer. This is extremely fortunate because that means that the `anytype` parameter for self prefers pointers first and we don’t have to worry about if `self` was copied by value and end up returning invalid memory on the stack (I also tested on `ReleaseFast`… same thing).

So this is very peculiar but it makes the pattern you wanted to do completely possible. Let’s write an `isConstPtr` and `asConst` helper functions and use that to internally dispatch (note: you could use the `@as` builtin instead, but I find it to be uglier)…

```zig
fn isConstPtr(comptime T: type) bool {
    return switch (@typeInfo(T)) {
        .Pointer => |p| p.is_const, else => false
    };
}

fn asConst(comptime T: type, value: *const T) @TypeOf(value) {
    return value;
}

const Foo = struct {
    value: usize = 0,
    pub fn getPtr(self: anytype) @TypeOf(&self.value) {
        if (comptime isConstPtr(@TypeOf(self))) {
            // do some things you don't want to duplicate
            return &self.value;
        } else {
            return @constCast(asConst(@This(), self).getPtr());
        }
    }
};

```

For casting arguments, here’s an example from bounded\_array.zig: [zig/lib/std/bounded\_array.zig at master · ziglang/zig · GitHub](https://github.com/ziglang/zig/blob/master/lib/std/bounded_array.zig)

```zig
 /// View the internal array as a slice whose size was previously set.
pub fn slice(self: anytype) switch (@TypeOf(&self.buffer)) {
    *align(alignment) [buffer_capacity]T => []align(alignment) T,
    *align(alignment) const [buffer_capacity]T => []align(alignment) const T,
    else => unreachable,
    } {
        return self.buffer[0..self.len];
    }

```

You can see here that they are determining the type with a switch statement and deciding which type of slice to return so you can get fancier in the return types than my simple example (especially if casting is involved).

So yes, you could do some serious deduplication with this pattern. I’m surprised this isn’t more well known but I guess that’s why we’re here to have this dicussion, lol.

Anyhow, I’m editing my other comment because I was incorrect about the status of member functions as they relate to this technique. I’m going to open up a brainstorming topic on this because I think we have some more experimenting to do 🙂

---

_[View the full topic](https://ziggit.dev/t/is-it-possible-to-apply-const-on-return-result-adaptively/3008)._
