Skip to content

de-genericify linked lists #23459

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Apr 4, 2025
Merged

de-genericify linked lists #23459

merged 4 commits into from
Apr 4, 2025

Conversation

andrewrk
Copy link
Member

@andrewrk andrewrk commented Apr 3, 2025

With these changes, there's no longer any incentive to hand-roll next/prev pointers. A little bit less bloat too.

Migration Instructions

std.DoublyLinkedList(T).Node

⬇️

struct {
    node: std.DoublyLinkedList.Node,
    data: T,
}

Then use @fieldParentPtr to get from node to data.

In many cases there's a better pattern instead which is to put the node intrusively into the data structure. If you're not already doing that, there's a good chance linked list is the wrong data structure.

andrewrk added 3 commits April 3, 2025 14:55
by making it always intrusive, we make it a more broadly useful API, and
avoid binary bloat.
by making it always intrusive, we make it more broadly useful API, and
avoid binary bloat.
this is trivial to tack on, and in my experience it is rarely wanted.
@andrewrk andrewrk added breaking Implementing this issue could cause existing code to no longer compile or have different behavior. standard library This issue involves writing Zig code for the standard library. release notes This PR should be mentioned in the release notes. labels Apr 3, 2025
@nikneym
Copy link
Contributor

nikneym commented Apr 4, 2025

These changes make sense, though I feel like need for @fieldParentPtr can be eliminated by forcing type T to have next: ?*T field in it's struct. We can also check for eligibility in comptime, e.g:

pub fn SinglyLinkedList(comptime T: type) type {
    comptime {
        if (@FieldType(T, "next") != ?*T) {
            @compileError("type T must satisfy next field with type ?*T");
        }
    }

    return struct { ... };
}

This is a typical pattern we see in large Zig projects too (libxev, TigerBeetle). That way, new implementation can attract projects to switch to stdlib implementation.

@mlugg
Copy link
Member

mlugg commented Apr 4, 2025

These changes make sense, though I feel like need for @fieldParentPtr can be eliminated by forcing type T to have next: ?*T field in it's struct.

This assumes that using @fieldParentPtr is bad in some way. It's a completely reasonable operation with good type safety; no need to complicate things with comptime logic and requiring things of the container.

@andrewrk andrewrk merged commit 8acedfd into master Apr 4, 2025
16 of 18 checks passed
@andrewrk andrewrk deleted the linked-lists branch April 4, 2025 21:48
next_node.prev = node.prev;
} else {
// Last element of the list.
list.last = node.prev;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if the node is a member of a different list? I dont see how that is asserted here.

Copy link
Contributor

@kavika13 kavika13 Apr 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It isn't asserted here, and the old code didn't either (it is basically the same code). So I think it's off topic for this PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
breaking Implementing this issue could cause existing code to no longer compile or have different behavior. release notes This PR should be mentioned in the release notes. standard library This issue involves writing Zig code for the standard library.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants