"self" implicitly promoted to pointer?

Below is a repro case that exhibits some surprising implicit pointer conversion.

Consider the line with const a = pool.c(3). Isn’t this promoting a Pool value to *Pool (pointer)? I’d expect a compiler error from the call pool.c(3) given how the Pool.c() function is declared to accept a Pool pointer.

In this case the code works but I can imagine cases where this behavior might mask away accidentally passing stuff by value when passing by reference was intended.

const std = @import("std");

pub const Pool = struct {
    pub const Node = struct {
        pub const Kind = enum {
        kind: Kind,
        data: f32 = 0,
    alloc: std.mem.Allocator,

    pub fn init(alloc: std.mem.Allocator) Pool {
        return Pool{ .alloc = alloc };

    pub fn allocNode(self: *@This()) *Node {
        return self.alloc.create(Node) catch unreachable;

    pub fn new(pool: *Pool, kind: Node.Kind, v: f32) *Node {
        var n = pool.allocNode();
        n.* = Node{ .kind = kind, .data = v };
        return n;

    pub fn c(p: *Pool, v: f32) *Node {
        return new(p, Node.Kind.c, v);

fn usePool(p: *Pool) void {
    const x = p.c(4);
    _ = x;

test "repro" {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    var pool = Pool.init(gpa.allocator());
    const a = pool.c(3); // why is this not an error? "this" implicitly promoted from Pool to *Pool??
    _ = a;

Yes, dot syntax will take a pointer of the “receiver” if that’s what the function asks for.

Can you make one such example? I personally can’t think of any footgun caused by this system.


Hmm, I thought I could but maybe it’s not a problem after all. The problematic cases I had in mind would break constness.