Learning Zig, day #1
Tags: zig, vim, neovim, go
Published: 2024-06-05 12:17:53

# The why
So I have decided to learn Zig. This is kind of an odd decision because I have never had any interest in Zig. But I have had an interest in Rust for years.  Just a little about my background.  I am a professional Go developer (well was I've been unemployed for 2 weeks). And during my downtime I decided to pick up a new programming language. This will be the 4th professional language I've learned. My first professional language being Java, then Python years later, now Go for the last 8 years. 

## Toy Program to get started

Anyway I plan writing my own message broker.  And for no reason other to see if I can. One of my favorite simple things to write in Go are TCP sockets. So figuring out how to do that in Zig was a priority, surprisingly it's not too bad.


So first they suggest that I create a new **_Ip4Address_**. Here it is below.

```zig
    const addr = net.Ip4Address.parse("127.0.0.1", 3000);
```

Alright not too painful. I then have to initialize an **_Address_**. This is a struct that takes in an Ip4Address, which I created above. So my next step kind of looks like this.

```zig
   var server =  try local_addr.listen(.{.reuse_port=true});
   defer server.deinit();
```

So what's happening here is pretty cool.  We call the "listen" method. Because this can return an error, we call it with "try". This looks suspiciously like an exception. But not to worry, errors are values in Zig, just like Golang.  This can alternatively be written like this.

```zig
var server =  local_addr.listen(.{.reuse_port=true}) catch |err|{
       //handle value
 };
 defer server.deinit();
```

So the **try** semantic is kind of syntactic sugar. Since I'm new to the language and this was my first day using it, I just went with try, but I can see using the "catch" version for better debugging and logging.

Now finally I'm ready to accept a connection, so lets start our loop.

```zig
    while(true){
        const conn = try server.accept();
        const message = try conn.stream.reader().readAllAlloc(reader_allocator, 1024);

        print("{s}", .{message});
        reader_allocator.free(message);   
    }

```

As you can see, we use the "try" syntactic sugar again. Then we obviously read from our connection.  What's important is the "readAllAlloc" call. It takes an allocator, and a size which is the size of the allocator.  I actually define an "allocator" earlier.  I won't get too much into allocators because I only have a super basic understanding of them.  But essentially instead of the ownership and borrow checker model found in Rust, we instead can create an allocator structure.  We can clean it up whenever we want, but it's usually done via a **defer**. I'm in a an infinite loop, meaning this will never reach the end of the function.  So I an deallocating manually with `reader_allocator.free(message)`. 

Here is the fully code:

```zig
const std = @import("std");
const net = std.net;
const print = std.debug.print;

pub fn main() !void {

    var gpa = std.heap.GeneralPurposeAllocator(.{}){};

    defer _ = gpa.deinit();

    const reader_allocator = gpa.allocator();

    const addr = try net.Ip4Address.parse("127.0.0.1", 3000);
    const local_addr = net.Address{.in = addr};

    var server =  try local_addr.listen(.{.reuse_port=true});
    defer server.deinit();

    while(true){
        const conn = try server.accept();
        const message = try conn.stream.reader().readAllAlloc(reader_allocator, 1024);

        print("{s}", .{message});
        reader_allocator.free(message);   
    }

}
```

##Thoughts and review so far

So playing around with Zig is actually pretty fun. I've had a lot of false starts with Rust, and it's just hard to get started with.  But Zig I was able to just start writing code right away. Zig, while simple, does have a learning curve.  But it seems to not be wearing its concepts on its sleeve like Rust. I also think the allocator strategy is genius.  And while the Rust borrow checker is also an incredibly good idea, it can make the ownership semantics extremely difficult to work with.

Clearly my program will need some improvements. While this compiles and works, I'm not sending anything back to the client. I also need to build a client as well.  Probably put the server in its own package.  And I need to figure out what to do with this allocator, because calling it in a loop probably isn't incredibly safe.  So I may need to free up memory based on a few conditions, like after a response to the client.

Anyway it's not everyday I'm excited about a new language.