Ziggy String literals, arrays, slices & WFT comptime
- Dominant language
- No language data
- Stars
- 4
- Forks
- 1
- PR merge metrics
- No merged PRs in 30d
Description
is a good place to search on ziggy problems.
## Explore types with `@TypeOf`
[building...]
### Vademecum
```zig
const std = @import("std");
const print = std.debug.print;
// my String type
const String: type = []const u8;
// function returning a type
fn isString() type {
return String;
}
test "What?" {
// a String literal is a const pointer to an array of characters
const elixir = "Elixir";
print("\nThe variable with value {s} is a string literal. Check the type: {}\n", .{ elixir, @TypeOf(elixir) });
var zig: String = "Zig";
zig = "zig"
std.debug.print("\nThe variable with value {s} is a String: {}\n", .{ zig, @TypeOf(zig) == isString() });
const ptr_elixir = &elixir;
const ptr_zig = &zig;
print("\n{}\t {}\t {s}\n", .{ @TypeOf(ptr_elixir), @TypeOf(ptr_elixir.*), ptr_elixir.* });
print("\n{}\t {s}\n", .{ @TypeOf(ptr_zig.*), ptr_zig.* });
try std.testing.expect(@TypeOf(l) == isString());
const word = [5]u8{ 'h', 'e', 'l', 'l', 'o' };
const slice_word = word[0..];
print("\nArrays: {s}\t {}\t {s}\t {}\n", .{ word, @TypeOf(word), slice_word, @TypeOf(slice_word) });
}
```
Run `zig test my_file.zig`:
```
The variable with value Elixir is a string literal. Check the type: *const [6:0]u8
The variable with value zig is a String? : true
*const [6:0]u8 Elixir
^^ the pointer is pointing to a const pointer pointing to an array of characters (1 byte, 8 bites)
*[]const u8 []const u8 Zig
# ^^ if we use "const zig = "Zig", then we get @TypeOf(&zig) == *const []const u8
# the type determines the presence of the first "const"
Arrays: hello [5]u8 hello *const [4]u8
All 1 tests passed.
```
### Lets summarise
- Arrays are `[n]T` of comptime known length. You can instantiate them:
`const word = [5]u8{'h', 'e', 'l', 'l', 'o'}` with type `[5:0]u8`
- Slices are `[]T` of types `T` with runtime known length. They are made of a pointer to the first element of an Array and a length.
> For example, when you allocate memory for `n` elements of type `T`, we get a slice:
`var slice_of_t: []T = allocator.alloc(T, n)`.
- You can instantiate a slice from an array: we check that we get a pointer
`const slice_word = word[0..]` with type `*const [4]u8`
- Recall that the type of a string literal is a pointer to an array.
`const zig = "zig"` has type `*const [3:0]u8`
Ok, now we test our understanding:
```zig
fn printIt(msg: []const u8) void {
print("{s}", .{msg});
}
fn testIt() [5]u8 {
return [5]u8{ 'h', 'e', 'l', 'l', 'o' };
}
```
We can use it like this:
```zig
test "print" {
printIt("zig")
printIt(&testIt());
^^
}
```
> ❗ `printIt` is expecting a slice, `[]const u8`, which is a pointer....
"zig" has type `*const [3:0]u8`, a pointer, ✅
since `testIt()` returns an `[5]u8`, an array, we need to pass `&testIt()` ✅
Now, can we mutate a literal string? Yes, dereference it!
```zig
test "mut literal" {
const Zig = "Zig";
var v_zig = Zig.*;
print("\n{}\t{s}\n", .{ @TypeOf(v_zig), v_zig });
v_zig[0] = 'P';
print("\n{}\t{s}\n", .{ @TypeOf(v_zig), v_zig });
std.testing.expectEqualStrings("Pig", &v_zig);
```
gives:
```
[3:0]u8 Zig
[3:0]u8 Pig
All 1 tests passed.
```
### Structs
Now consider using a struct. Pointers **are** needed here when you use functions to modify.
```zig
const Person = struct { salary: i16};
fn increase(p: *Person, amount: i16) void {
^^
p.salary += amount;
}
test "struct" {
var me = Person{ .salary = 100};
std.debug.print("{}", .{me.salary});
increase(&me, 100);
^^
std.debug.print("{}", .{me.salary});
me.salary += 100;
std.debug.print("{}", .{me.salary});
try std.testing.expectEqual(me.salary, 300);
}
```
We get:
```
100
200
300
All 1 tests passed.
```
> ❗ We **need** to use a pointer to pass the struct `Person` to the function "increase".
Indeed, the arguments of a function are immutable, so inside the function, we can't modify the `p` we get.
=> we get: "error: cannot assign to constant"
When we pass a pointer, the pointer is not modified inside the function. However, we can mutate the data at which the pointer is pointing at. We don't need to deference the pointer: `p.salary` works fine.
Now consider a struct that holds internal methods. Again, when the method modifies one of the fields of the struct, you must use a pointer. Note that the inner method uses a reflection.
```zig
const Person = struct {
salary: i16,
fn double(p: *Person) void {
p.salary *= 2;
}
};
test "struct" {
var me = Person{ .salary = 100 };
std.debug.print("\n{}\n", .{me.salary});
increase(&me, 100);
std.debug.print("\n{}\n", .{me.salary});
me.double();
std.debug.print("\n{}\n", .{me.salary});
me.salary += 100;
std.debug.print("\n{}\n", .{me.salary});
try std.testing.expectEqual(me.salary, 500);
}
```
We get as expected:
```
100
200
400
500
All 1 tests passed.
```
### Pause
Check this video:
[](https://www.youtube.com/watch?v=VgjRyaRTH6E&t=1s)
## WFT Comptime....
❗ References to keep!
A first link:
[](https://kristoff.it/blog/what-is-zig-comptime/)
Then, a GitHub link:
[](https://github.com/edyu/wtf-zig-comptime)
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.