dwyl / dwyl/learn-zig

Ziggy String literals, arrays, slices & WFT comptime

Open
#8 1 comment 2 reactions 0 assignees View on GitHub
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:

[Screenshot 2024-10-21 at 13 59 09](https://www.youtube.com/watch?v=VgjRyaRTH6E&t=1s)

## WFT Comptime....

❗ References to keep!

A first link:

[Screenshot 2024-10-21 at 12 57 13](https://kristoff.it/blog/what-is-zig-comptime/)

Then, a GitHub link:

[Screenshot 2024-10-21 at 12 58 21](https://github.com/edyu/wtf-zig-comptime)

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.