Ideas For a Project to Learn Zig

Hi all! I’m looking for a project idea to learn Zig with. I’m particularly interested in exploring the limits of comptime, changing the way functions work based on inputs, ala std.debug.print.

Things I’ve already tried:

  • Advent of Code: A lot of parsing, and basic math calculations. I’ve learned, but I’m looking for a different challenge.
  • I tried putting my own spin on nine-lives-later/zzmq; I didn’t understand why it needed allocators, but when I tried doing it, I had problems passing data to c. (zmq_send_const and zmq_recv), and once I got past that, I hit a bunch of frustrating hangs with no obvious solutions.

The omission of macros is one of the things that drew me to Zig (and away from rust), and I’m keen to see how many of the benefits of macros you can get with just comptime.

Thanks in Advance!

2 Likes

In-memory relational database with a comptime based ORM?

Something like this:

const std = @import("std");

fn DBType(comptime schema: anytype) type {
    // comptime magic here
}

const Account = struct {
    id: ID,
    balance: u128,

    const ID = enum(u64) { _ };
};

const Transfer = struct {
    id: ID,
    amount: u128,
    debit_account: Account.ID,
    credit_account: Account.ID,

    const ID = enum(u64) { _ };
};

// This is our database
const DB = DBType(.{
    .tables = .{
        .account = Account,
        .transfer = Transfer,
    },
    .indexes = .{
        .transfer = .{
            .debit_account, .credit_account,
        },
    },
});

pub fn main() void {
    var gpa_allocator: std.heap.GeneralPurposeAllocator(.{}) = .{};
    const gpa = gpa_allocator.allocator();

    var db = DB.init(gpa);
    defer db.deinit(gpa);

    // Create individual objects
    const alice = db.accont().create(.{ .balance = 100 });
    const bob = db.accont().create(.{ .balance = 200 });
    transfer(db, alice, bob, 100);

    // Index-based lookups
    var alice_transfers_itertor = db.transfer().filter(.{ .debit_account = alice });
    while (alice_transfers_itertor.next()) {}
}

fn transfer(
    db: *DB,
    debit_account: Account.ID,
    credit_account: Account.ID,
    amount: u128,
) ?Transfer.ID {
    // Point lookups
    const dr = db.account().get(debit_account) orelse return null;
    const cr = db.account().get(credit_account) orelse return null;
    if (dr.balance >= amount and cr.balance <= std.math.maxInt(u128) - amount) {
        // Point updates
        db.account().update(debit_account, .{ .balance = dr.balance - amount });
        db.account().update(credit_account, .{ .balance = dr.balance - amount });
        return db.transfer().create(.{
            .debit_account = debit_account,
            .credit_account = credit_account,
            .amount = amount,
        });
    }
    return null;
}
3 Likes