Intrusive lists
Zig's std lists are intrusive.
Do not be afraid. Go ahead.
Non-intrusive and intrusive
A non-intrusive list wraps your item in its own node.
- The container makes the node.
- It stores a copy of your item in it.
An intrusive list keeps its link inside your struct.
- The link is a field of your struct: a Node.
- The list keeps a pointer to that Node.
- It never copies your struct.
In Zig, with a plain std list:
const Message: type = struct {
text: []const u8,
node: std.DoublyLinkedList.Node = .{},
};
const Job: type = struct {
id: u32,
node: std.DoublyLinkedList.Node = .{},
};
What you get
| typed container of values | intrusive list | |
|---|---|---|
| add an item | allocates. Can fail: try |
allocates nothing. Cannot fail |
| your struct | copied into the container | stays where it is |
| a pointer into your struct | breaks when the container grows | stays valid |
| a struct with a mutex, or too large to copy | does not fit | fits |
| remove an item from the middle | search for it, then shift the rest | one call, no search |
| move an item to another list | copy it, and maybe allocate | remove, append. No allocation |
Some structs must not be copied.
- It has a mutex.
- Other code points into it.
- It is too large to copy.
Such a struct can still be in an intrusive list.
What it costs
The list does not know your struct.
- You get your struct back with
@fieldParentPtr. @fieldParentPtrtrusts you. A wrong guess is not caught.
The memory is yours.
- The list does not free it.
- The list does not know when the struct is gone.
If you come from C, this is Linux's list_head with container_of.
Paternitas does not change any of this.
It adds one thing: before you get your struct back, it checks the type.