1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
|
//
// A sentinel value indicates the end of data. Let's imagine a
// sequence of lowercase letters where uppercase 'S' is the
// sentinel, indicating the end of the sequence:
//
// abcdefS
//
// If our sequence also allows for uppercase letters, 'S' would
// make a terrible sentinel since it could no longer be a regular
// value in the sequence:
//
// abcdQRST
// ^-- Oops! The last letter in the sequence is R!
//
// A popular choice for indicating the end of a string is the
// value 0. ASCII and Unicode call this the "Null Character".
//
// Zig supports sentinel-terminated arrays, slices, and pointers:
//
// const a: [4:0]u32 = [4:0]u32{1, 2, 3, 4};
// const b: [:0]const u32 = &[4:0]u32{1, 2, 3, 4};
// const c: [*:0]const u32 = &[4:0]u32{1, 2, 3, 4};
//
// Array 'a' stores 5 u32 values, the last of which is 0.
// However the compiler takes care of this housekeeping detail
// for you. You can treat 'a' as a normal array with just 4
// items.
//
// Slice 'b' is only allowed to point to zero-terminated arrays
// but otherwise works just like a normal slice.
//
// Pointer 'c' is exactly like the many-item pointers we learned
// about in exercise 054, but it is guaranteed to end in 0.
// Because of this guarantee, we can safely find the end of this
// many-item pointer without knowing its length. (We CAN'T do
// that with regular many-item pointers!).
//
// Important: the sentinel value must be of the same type as the
// data being terminated!
//
const print = @import("std").debug.print;
pub fn main() void {
// Here's a zero-terminated array of u32 values:
var nums = [_:0]u32{ 1, 2, 3, 4, 5, 6 };
// And here's a zero-terminated many-item pointer:
var ptr: [*:0]u32 = &nums;
// For fun, let's replace the value at position 3 with the
// sentinel value 0. This seems kind of naughty.
nums[3] = 0;
// So now we have a zero-terminated array and a many-item
// pointer that reference the same data: a sequence of
// numbers that both ends in and CONTAINS the sentinel value.
//
// Attempting to loop through and print both of these should
// demonstrate how they are similar and different.
//
// (It turns out that the array prints completely, including
// the sentinel 0 in the middle. The many-item pointer must
// stop at the first sentinel value. The difference is simply
// that arrays have a known length and many-item pointers
// don't.)
printSequence(nums);
printSequence(ptr);
print("\n", .{});
}
// Here's our generic sequence printing function. It's nearly
// complete, but there are a couple missing bits. Please fix
// them!
fn printSequence(my_seq: anytype) void {
const my_type = @typeInfo(@TypeOf(my_seq));
// The TypeInfo contained in my_type is a union. We use a
// switch to handle printing the Array or Pointer fields,
// depending on which type of my_seq was passed in:
switch (my_type) {
.Array => {
print("Array:", .{});
// Loop through the items in my_seq.
for (???) |s| {
print("{}", .{s});
}
},
.Pointer => {
// Check this out - it's pretty cool:
const my_sentinel = my_type.Pointer.sentinel;
print("Many-item pointer:", .{});
// Loop through the items in my_seq until we hit the
// sentinel value.
var i: usize = 0;
while (??? != my_sentinel) {
print("{}", .{my_seq[i]});
i += 1;
}
},
else => unreachable,
}
print(". ", .{});
}
|