BitSet provides efficient storage and manipulation of sets of integers represented as bits.
Type Variants
Zig provides three BitSet types:
- StaticBitSet - Fixed size known at compile time
- DynamicBitSet - Runtime-sized with allocator
- DynamicBitSetUnmanaged - Runtime-sized without storing allocator
StaticBitSet
pub fn StaticBitSet(comptime size: usize) type
Fixed-size bit set with compile-time known size.
Creation
const BitSet = std.bit_set.StaticBitSet(128);
var bits = BitSet.initEmpty();
var all_set = BitSet.initFull();
Methods
initEmpty
pub fn initEmpty() StaticBitSet(size)
Creates a bit set with all bits unset.
initFull
pub fn initFull() StaticBitSet(size)
Creates a bit set with all bits set.
set
pub fn set(self: *StaticBitSet(size), index: usize) void
Sets the bit at the given index.
unset
pub fn unset(self: *StaticBitSet(size), index: usize) void
Clears the bit at the given index.
toggle
pub fn toggle(self: *StaticBitSet(size), index: usize) void
Toggles the bit at the given index.
isSet
pub fn isSet(self: StaticBitSet(size), index: usize) bool
Returns whether the bit at the given index is set.
count
pub fn count(self: StaticBitSet(size)) usize
Returns the number of set bits (population count).
setAll
pub fn setAll(self: *StaticBitSet(size)) void
Sets all bits.
unsetAll
pub fn unsetAll(self: *StaticBitSet(size)) void
Clears all bits.
toggleAll
pub fn toggleAll(self: *StaticBitSet(size)) void
Toggles all bits.
Set Operations
setUnion
pub fn setUnion(self: StaticBitSet(size), other: StaticBitSet(size)) StaticBitSet(size)
Returns the union of two bit sets (OR operation).
setIntersection
pub fn setIntersection(self: StaticBitSet(size), other: StaticBitSet(size)) StaticBitSet(size)
Returns the intersection of two bit sets (AND operation).
setDifference
pub fn setDifference(self: StaticBitSet(size), other: StaticBitSet(size)) StaticBitSet(size)
Returns the difference (bits in self but not in other).
toggleSet
pub fn toggleSet(self: *StaticBitSet(size), other: StaticBitSet(size)) void
Toggles all bits that are set in other (XOR operation).
Iteration
iterator
pub fn iterator(self: *const StaticBitSet(size)) Iterator
Returns an iterator over set bit indices.
var it = bits.iterator(.{});
while (it.next()) |index| {
std.debug.print("Bit {} is set\n", .{index});
}
Usage Example
const std = @import("std");
const StaticBitSet = std.bit_set.StaticBitSet;
pub fn main() void {
const BitSet = StaticBitSet(64);
var bits = BitSet.initEmpty();
// Set some bits
bits.set(5);
bits.set(10);
bits.set(42);
std.debug.print("Count: {}\n", .{bits.count()}); // 3
std.debug.print("Is 5 set? {}\n", .{bits.isSet(5)}); // true
std.debug.print("Is 7 set? {}\n", .{bits.isSet(7)}); // false
// Set operations
var other = BitSet.initEmpty();
other.set(5);
other.set(15);
const union_bits = bits.setUnion(other);
std.debug.print("Union count: {}\n", .{union_bits.count()}); // 4
const intersection = bits.setIntersection(other);
std.debug.print("Intersection count: {}\n", .{intersection.count()}); // 1
// Iterate
var it = bits.iterator(.{});
while (it.next()) |index| {
std.debug.print("Set: {}\n", .{index});
}
}
DynamicBitSet
Dynamically-sized bit set with an allocator.
Creation and Destruction
initEmpty
pub fn initEmpty(allocator: Allocator, bit_length: usize) !DynamicBitSet
Creates an empty bit set with the specified capacity.
initFull
pub fn initFull(allocator: Allocator, bit_length: usize) !DynamicBitSet
Creates a bit set with all bits set.
deinit
pub fn deinit(self: *DynamicBitSet) void
Frees the bit set’s memory.
Resizing
resize
pub fn resize(self: *DynamicBitSet, new_len: usize, value: bool) !void
Resizes the bit set. New bits are set to value.
Usage Example
const std = @import("std");
const DynamicBitSet = std.bit_set.DynamicBitSet;
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var bits = try DynamicBitSet.initEmpty(allocator, 100);
defer bits.deinit();
bits.set(50);
bits.set(75);
std.debug.print("Count: {}\n", .{bits.count()});
// Resize
try bits.resize(200, false);
bits.set(150);
}
DynamicBitSetUnmanaged
pub const DynamicBitSetUnmanaged
Dynamically-sized bit set that doesn’t store the allocator.
All methods that allocate require passing the allocator explicitly.
Methods
Same as DynamicBitSet, but allocator is passed to each method:
pub fn initEmpty(allocator: Allocator, bit_length: usize) !DynamicBitSetUnmanaged
pub fn deinit(self: *DynamicBitSetUnmanaged, allocator: Allocator) void
pub fn resize(self: *DynamicBitSetUnmanaged, allocator: Allocator, new_len: usize, value: bool) !void
Use Cases
Tracking Visited Nodes
var visited = StaticBitSet(graph.node_count).initEmpty();
for (nodes_to_visit) |node_id| {
if (!visited.isSet(node_id)) {
visited.set(node_id);
// Process node
}
}
Flags and Permissions
const Permissions = StaticBitSet(8);
const READ = 0;
const WRITE = 1;
const EXECUTE = 2;
var perms = Permissions.initEmpty();
perms.set(READ);
perms.set(WRITE);
if (perms.isSet(WRITE)) {
// Allow write
}
Bloom Filters
var bloom = try DynamicBitSet.initEmpty(allocator, 1000);
defer bloom.deinit();
const hash1 = hashFunction1(key) % 1000;
const hash2 = hashFunction2(key) % 1000;
bloom.set(hash1);
bloom.set(hash2);
- Set/Unset/Toggle: O(1)
- Count: O(n) where n is number of machine words
- Set operations: O(n) where n is number of machine words
- Memory: ⌈size/8⌉ bytes plus overhead
BitSet operations are highly optimized using CPU instructions like popcnt for counting set bits.
- EnumSet - Type-safe bit set for enums (see std namespace)
- HashMap - When you need to map keys to boolean values