Skip to content

Commit 76642fc

Browse files
committed
Restructure repo, add distinct_integers
1 parent cda307d commit 76642fc

8 files changed

+68
-20
lines changed

README.md

+13-13
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,29 @@
1-
# Zig Design Patterns
1+
# Zig Patterns
22

3-
- [Zig Design Patterns](#zig-design-patterns)
3+
- [Zig Patterns](#zig-patterns)
44
- [About](#about)
5-
- [Examples](#examples)
5+
- [The Patterns](#the-patterns)
66
- [License](#license)
77

88
## About
99

10-
This repository contains examples of common design patterns found in Zig's standard library and many community projects.
10+
This repository contains examples of common patterns found in Zig's standard library and many community projects.
1111

1212
Note that copying (one of) these patterns verbatim into your project may not be useful; it's important to weigh the pros and cons of different patterns. If you come from another language, especially a non-systems one, you'll often find that the more unfamiliar ideas featured here may be more useful.
1313

14-
For example, I've seldom used interface-like patterns when designing systems in Zig, despite using such patterns dozens of times a day at work writing TypeScript or Go.
14+
For example, I've seldom used interface-like patterns when designing systems in Zig, despite using such patterns dozens of times a day at my old job writing TypeScript or Go.
1515

16-
## Examples
16+
## The Patterns
1717

1818
All examples are annotated with comments including recommendations regarding when to use the patterns shown.
1919

20-
| Example | How to run |
21-
| ----- | --- |
22-
| [`@fieldParentPtr`](src/field_parent_ptr.zig) | `zig build field_parent_ptr` |
23-
| [Inline Switch](src/inline_switch.zig) | `zig build inline_switch` |
24-
| [MultiArrayList](src/multi_array_list.zig) | `zig build multi_array_list` |
25-
| [Type Function](src/type_function.zig) | `zig build type_function` |
26-
| [Virtual Table](src/vtable.zig) | `zig build vtable` |
20+
Example tests can be run with `zig build [name_of_pattern]`. For example, to run the tests in `typing/type_function.zig`, run `zig build type_function`.
21+
22+
Patterns are grouped into the following categories:
23+
- `data` - data layout and organization techniques
24+
- `typing` - `comptime`, runtime, or mixed `type` or type safety techniques
25+
26+
Some patterns may belong in multiple categories; I've selected the most fitting one in my opinion.
2727

2828
## License
2929

build.zig

+6-5
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
const std = @import("std");
22

33
pub const examples = .{
4-
.{ "field_parent_ptr", "src/field_parent_ptr.zig" },
5-
.{ "inline_switch", "src/inline_switch.zig" },
6-
.{ "multi_array_list", "src/multi_array_list.zig" },
7-
.{ "type_function", "src/type_function.zig" },
8-
.{ "vtable", "src/vtable.zig" },
4+
.{ "field_parent_ptr", "src/typing/field_parent_ptr.zig" },
5+
.{ "inline_switch", "src/typing/inline_switch.zig" },
6+
.{ "multi_array_list", "src/data/multi_array_list.zig" },
7+
.{ "type_function", "src/typing/type_function.zig" },
8+
.{ "vtable", "src/typing/vtable.zig" },
9+
.{ "distinct_integers", "src/typing/distinct_integers.zig" },
910
};
1011

1112
pub fn build(b: *std.Build) void {
File renamed without changes.

src/typing/distinct_integers.zig

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
//! When designing systems in Zig that take indices, it's important to
2+
//! ensure indices are not accidentally mixed up. For example, if I have
3+
//! a list `A` of length 10 and a list `B` of length 20, it is safe to
4+
//! access `A` with any number representing an index into `A`, and `B`
5+
//! with any number representing an index into `B`, but if we were to,
6+
//! for example, access `A` with a number representing an index into `B`,
7+
//! we could access garbage information or cause an out of bounds accesss.
8+
//!
9+
//! A real world example I've run into while working on ZLS is accidentally
10+
//! swapping `TokenIndex`s and `Ast.Node.Index`s, which are both `= u32`.
11+
//!
12+
//! ```zig
13+
//! pub fn accessANodeNotDistinct(node_index: u32) void {
14+
//! // ...
15+
//! }
16+
//!
17+
//! const token_index_not_a_node: u32 = 10;
18+
//! accessANodeNotDistinct(token_index_not_a_node);
19+
//!
20+
//! // kabloom!
21+
//! ```
22+
//!
23+
//! How can we avoid this? Distinct integers!
24+
25+
const std = @import("std");
26+
27+
pub const TokenIndex = enum(u32) {_};
28+
pub const NodeIndex = enum(u32) {_};
29+
30+
pub fn lastToken() TokenIndex {
31+
return @enumFromInt(0);
32+
}
33+
34+
pub fn lastNode() NodeIndex {
35+
return @enumFromInt(0);
36+
}
37+
38+
pub fn accessANodeDistinct(node_index: NodeIndex) void {
39+
// do node things
40+
_ = node_index;
41+
}
42+
43+
test {
44+
accessANodeDistinct(lastNode());
45+
// uncomment the line below this one and the program won't compile:
46+
// accessANodeDistinct(lastToken());
47+
}

src/field_parent_ptr.zig renamed to src/typing/field_parent_ptr.zig

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ pub const Cat = struct {
2424
favorite_food_brand: []const u8,
2525

2626
pub fn sayHello(animal: *const Animal) void {
27-
const cat = @fieldParentPtr(Cat, "animal", animal);
27+
const cat: *const Cat = @fieldParentPtr("animal", animal);
2828
std.debug.print("Hi my name is {s} and I only eat {s}\n", .{ animal.name, cat.favorite_food_brand });
2929
}
3030
};
@@ -35,7 +35,7 @@ pub const Dog = struct {
3535
favorite_dog_toy: []const u8,
3636

3737
pub fn sayHello(animal: *const Animal) void {
38-
const dog = @fieldParentPtr(Dog, "animal", animal);
38+
const dog: *const Dog = @fieldParentPtr("animal", animal);
3939
std.debug.print("Hi my name is {s} and my favorite dog toy is {s}\n", .{ animal.name, dog.favorite_dog_toy });
4040
}
4141
};
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)