Call stack based 'array'

Working on my chess engine, and studying Stockfish, I find some intriguing situations.
The chessboard contains a pointer to a StateInfo struct, which has a pointer prev pointing to the previous StateInfo.
When doing a move inside the search they just declare a local state and pass that to position.do_move().

I find that at least interesting. The “array” of StateInfo structs seems only to exist inside the (recursive) function callstack.

Would that same trick also work in ZIg?

const StateInfo = struct {
    // fields...
    prev: ?*StateInfo
}

Yes, not sure why you think it might not work?

It is possible that tail call optimisation could break it, but I doubt you’ll be doing such simple recursion, and I’m not sure if zig even enables that optimisation.

Yes, such a trick works. I used it for debug info in my code for parsing xkb keymaps:

pub const DebugInfo = struct {
    parser: *Parser,
    parent_info: ?*DebugInfo = null,
    token_context: ?TokenIndex = null,
};


pub fn addDebugInfo(this: *@This(), parent_info: ?*DebugInfo, token: TokenIndex) DebugInfo {
    return .{
        .parser = this,
        .parent_info = parent_info,
        .token_context = token,
    };
}

Then I create a new debug info variable whenever I want to add a level of context:

    var debug_info = this.addDebugInfo(parent_debug_info, keyname_token_index);

I don’t think the variable needs to be mutable, it should be enough to use a const pointer; I don’t remember why I chose to use a mutable pointer in this case.

Ok clear.
I actually run into a different problem I think. That is why I thought it did not work.
When creating an Engine with init(), the addresses of pos.state and &engine.history[0] are not the same.

pub const Engine = struct
{
    /// The one and only history for the position.
    /// * Search threads use a position copy without history.
    history: [1024]StateInfo,
    /// The one and only root position.
    pos: Position,

    pub fn init() Engine
    {
        var engine: Engine = undefined;
        engine.history = @splat(StateInfo.empty);
        engine.pos = .new(&engine.history[0]); // put the state pointer in pos
        return engine;
    }

EDIT: when I do not use init() the addresses are the same.

pub fn uci_run() !void
{
    var engine: search.Engine = undefined; // no init call
    engine.history = @splat(.empty);
    engine.pos = .new(&engine.history[0]);
    defer engine.deinit();

    while (true) { do our things... }
}

Because it was pointing to stack memory in init which is no longer valid when it finished and was overwritten

Yep I noticed it… Always ask “Where are the bytes?”