Zig 示例

Zig编程语言示例 - 简单、高效的系统编程,具有内存安全性

💻 Zig Hello World zig

🟢 simple ⭐⭐

Zig Hello World程序和基础语法特性

⏱️ 15 min 🏷️ zig, programming, system, memory-safe
Prerequisites: Basic programming concepts
// Zig Hello World Examples

// 1. Basic Hello World
const std = @import("std");

pub fn main() void {
    std.debug.print("Hello, World!\n", .{});
}

// 2. Hello World with variables and types
pub fn helloWithVariables() void {
    // String literals
    const message1 = "Hello, World!";
    const message2 = "Hello, Zig!";

    std.debug.print("{s}\n", .{message1});
    std.debug.print("{s}\n", .{message2});

    // Basic data types
    const integer: i32 = 42;
    const unsigned_integer: u32 = 100;
    const floating_point: f64 = 3.14159;
    const boolean: bool = true;
    const character: u8 = 'A';

    std.debug.print("Integer: {}\n", .{integer});
    std.debug.print("Unsigned: {}\n", .{unsigned_integer});
    std.debug.print("Float: {d}\n", .{floating_point});
    std.debug.print("Boolean: {}\n", .{boolean});
    std.debug.print("Character: {c}\n", .{character});
}

// 3. Hello World with functions
fn sayHello() []const u8 {
    return "Hello, World!";
}

fn greetUser(name: []const u8) []const u8 {
    // Note: This is simplified - in real code you'd use proper string formatting
    return "Hello!";
}

pub fn helloWithFunctions() void {
    std.debug.print("{s}\n", .{sayHello()});
    std.debug.print("{s}\n", .{greetUser("Zig")});
}

// 4. Hello World with structs
const User = struct {
    name: []const u8,
    age: u32,

    fn greet(self: User) void {
        std.debug.print("Hello, {s}! You are {} years old.\n", .{ self.name, self.age });
    }
};

pub fn helloWithStructs() void {
    const user = User{
        .name = "Alice",
        .age = 30,
    };

    user.greet();
}

// 5. Hello World with enums
const GreetingType = enum {
    formal,
    casual,
    enthusiastic,

    fn getText(self: GreetingType) []const u8 {
        return switch (self) {
            .formal => "Good day",
            .casual => "Hello",
            .enthusiastic => "Hey there!",
        };
    }
};

pub fn helloWithEnums() void {
    const formal_greeting = GreetingType.formal;
    const casual_greeting = GreetingType.casual;
    const enthusiastic_greeting = GreetingType.enthusiastic;

    std.debug.print("{s}, World!\n", .{formal_greeting.getText()});
    std.debug.print("{s}, World!\n", .{casual_greeting.getText()});
    std.debug.print("{s}, World!\n", .{enthusiastic_greeting.getText()});
}

// 6. Hello World with arrays and slices
pub fn helloWithArrays() void {
    // Array (fixed size, known at compile time)
    const greetings = [3][]const u8{
        "Hello",
        "Hola",
        "Bonjour"
    };

    // Loop through array
    for (greetings) |greeting| {
        std.debug.print("{s}, World!\n", .{greeting});
    }

    // Slice (runtime-sized view into array)
    const slice = greetings[0..2];
    std.debug.print("Slice length: {}\n", .{slice.len});
}

// 7. Hello World with error handling
const GreetingError = error{
    EmptyName,
    InvalidAge,
};

fn createGreeting(name: []const u8, age: u32) ![]const u8 {
    if (name.len == 0) return GreetingError.EmptyName;
    if (age == 0) return GreetingError.InvalidAge;

    // In real code, you'd use a proper allocator for string formatting
    return "Hello!";
}

pub fn helloWithErrorHandling() void {
    const result = createGreeting("Bob", 25);

    if (result) |greeting| {
        std.debug.print("{s}\n", .{greeting});
    } else |err| {
        std.debug.print("Error: {}\n", .{err});
    }
}

// 8. Hello World with comptime (compile-time execution)
pub fn helloWithComptime() void {
    // Comptime strings
    const comptime_greeting = comptime "Hello from compile time!";
    std.debug.print("{s}\n", .{comptime_greeting});

    // Comptime function execution
    const comptime_result = comptime addNumbers(5, 3);
    std.debug.print("Comptime 5 + 3 = {}\n", .{comptime_result});
}

fn addNumbers(a: i32, b: i32) i32 {
    return a + b;
}

// 9. Hello World with generic types
fn printGeneric(comptime T: type, value: T) void {
    switch (@typeInfo(T)) {
        .Int => std.debug.print("Integer: {}\n", .{value}),
        .Float => std.debug.print("Float: {d}\n", .{value}),
        .Bool => std.debug.print("Boolean: {}\n", .{value}),
        else => std.debug.print("Other type\n", .{}),
    }
}

pub fn helloWithGenerics() void {
    const int_value: i32 = 42;
    const float_value: f64 = 3.14;
    const bool_value: bool = true;

    printGeneric(i32, int_value);
    printGeneric(f64, float_value);
    printGeneric(bool, bool_value);
}

// 10. Hello World with optionals
fn findGreeting(id: u32) ?[]const u8 {
    return switch (id) {
        1 => "Hello",
        2 => "Hola",
        3 => "Bonjour",
        else => null,
    };
}

pub fn helloWithOptionals() void {
    const greeting1 = findGreeting(1) orelse "Default greeting";
    const greeting2 = findGreeting(99) orelse "Default greeting";

    std.debug.print("Found: {s}\n", .{greeting1});
    std.debug.print("Found: {s}\n", .{greeting2});
}

// Main function demonstrating all examples
pub fn main() void {
    std.debug.print("=== Zig Hello World Examples ===\n\n");

    std.debug.print("1. Basic Hello World:\n");
    std.debug.print("Hello, World!\n\n");

    std.debug.print("2. Variables and types:\n");
    helloWithVariables();
    std.debug.print("\n");

    std.debug.print("3. Functions:\n");
    helloWithFunctions();
    std.debug.print("\n");

    std.debug.print("4. Structs:\n");
    helloWithStructs();
    std.debug.print("\n");

    std.debug.print("5. Enums:\n");
    helloWithEnums();
    std.debug.print("\n");

    std.debug.print("6. Arrays and slices:\n");
    helloWithArrays();
    std.debug.print("\n");

    std.debug.print("7. Error handling:\n");
    helloWithErrorHandling();
    std.debug.print("\n");

    std.debug.print("8. Comptime execution:\n");
    helloWithComptime();
    std.debug.print("\n");

    std.debug.print("9. Generic types:\n");
    helloWithGenerics();
    std.debug.print("\n");

    std.debug.print("10. Optionals:\n");
    helloWithOptionals();
    std.debug.print("\n");

    std.debug.print("=== All Zig Examples Completed ===\n");
}

💻 Zig 内存管理 zig

🟡 intermediate ⭐⭐⭐⭐

Zig高级内存管理,包括分配器、切片和内存安全

⏱️ 25 min 🏷️ zig, memory, allocators, system-programming
Prerequisites: Basic Zig syntax and pointers
// Zig Memory Management Examples

const std = @import("std");

// 1. Stack allocation
pub fn stackAllocation() void {
    std.debug.print("=== Stack Allocation ===\n");

    // Variables allocated on stack (automatically managed)
    var number: i32 = 42;
    var boolean: bool = true;
    var array: [10]u8 = .{0} ** 10;

    array[0] = 1;
    array[1] = 2;

    std.debug.print("Stack number: {}\n", .{number});
    std.debug.print("Stack array: {any}\n", .{array[0..5]});
    std.debug.print("Memory freed automatically when function exits\n\n");
}

// 2. Heap allocation with allocator
pub fn heapAllocation(allocator: std.mem.Allocator) !void {
    std.debug.print("=== Heap Allocation ===\n");

    // Allocate slice on heap
    const slice = try allocator.alloc(u8, 10);
    defer allocator.free(slice);

    // Initialize the slice
    for (slice, 0..) |*byte, i| {
        byte.* = @intCast(i + 1);
    }

    std.debug.print("Heap slice: {any}\n", .{slice});
    std.debug.print("Slice length: {}\n", .{slice.len});
    std.debug.print("Slice capacity: {}\n", .{slice.capacity});
    std.debug.print("Memory will be freed by defer\n\n");
}

// 3. Custom allocator example
const CustomAllocator = struct {
    const Self = @This();

    buffer: [1024]u8,
    offset: usize,

    pub fn init() Self {
        return Self{
            .buffer = std.mem.zeroes([1024]u8),
            .offset = 0,
        };
    }

    pub fn allocator(self: *Self) std.mem.Allocator {
        return .{
            .ptr = self,
            .vtable = &.{
                .alloc = alloc,
                .resize = resize,
                .free = free,
            },
        };
    }

    fn alloc(ctx: *anyopaque, len: usize, log2_align: u8, ret_addr: usize) ?[*]u8 {
        _ = log2_align;
        _ = ret_addr;
        const self: *Self = @ptrCast(@alignCast(ctx));

        if (self.offset + len > self.buffer.len) return null;

        const result = self.buffer[self.offset..][0..len];
        self.offset += len;
        return result.ptr;
    }

    fn resize(ctx: *anyopaque, old_mem: []u8, log2_align: u8, new_len: usize) bool {
        _ = ctx;
        _ = old_mem;
        _ = log2_align;
        _ = new_len;
        return false; // Simple allocator doesn't support resizing
    }

    fn free(ctx: *anyopaque, old_mem: []u8, log2_align: u8, ret_addr: usize) void {
        _ = ctx;
        _ = old_mem;
        _ = log2_align;
        _ = ret_addr;
        // This simple allocator doesn't actually free memory
    }
};

pub fn customAllocatorDemo() !void {
    std.debug.print("=== Custom Allocator Demo ===\n");

    var custom_alloc = CustomAllocator.init();
    const allocator = custom_alloc.allocator();

    const slice = try allocator.alloc(u8, 20);
    defer allocator.free(slice);

    for (slice, 0..) |*byte, i| {
        byte.* = @intCast(i % 256);
    }

    std.debug.print("Custom allocated slice: {any}\n", .{slice[0..10]});
    std.debug.print("Allocator offset: {}\n", .{custom_alloc.offset});
    std.debug.print("\n");
}

// 4. Arena allocator
pub fn arenaAllocatorDemo() !void {
    std.debug.print("=== Arena Allocator Demo ===\n");

    var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
    defer arena.deinit();
    const allocator = arena.allocator();

    // Multiple allocations that will be freed all at once
    const slice1 = try allocator.alloc(u8, 100);
    const slice2 = try allocator.alloc(i32, 50);
    const slice3 = try allocator.alloc(f64, 25);

    std.debug.print("Allocated {} bytes for slice1\n", .{slice1.len});
    std.debug.print("Allocated {} integers for slice2\n", .{slice2.len});
    std.debug.print("Allocated {} floats for slice3\n", .{slice3.len});
    std.debug.print("All memory freed when arena is destroyed\n\n");
}

// 5. Fixed buffer allocator
pub fn fixedBufferAllocatorDemo() !void {
    std.debug.print("=== Fixed Buffer Allocator Demo ===\n");

    var buffer: [1024]u8 = undefined;
    var fba = std.heap.FixedBufferAllocator.init(&buffer);
    const allocator = fba.allocator();

    const slice = try allocator.alloc(u8, 100);
    defer allocator.free(slice);

    for (slice, 0..) |*byte, i| {
        byte.* = @intCast((i * 2) % 256);
    }

    std.debug.print("Fixed buffer slice: {any}\n", .{slice[0..20]});
    std.debug.print("Remaining buffer space: {} bytes\n", .{fba.end_index});
    std.debug.print("\n");
}

// 6. Memory-safe string operations
pub fn safeStringOperations(allocator: std.mem.Allocator) !void {
    std.debug.print("=== Safe String Operations ===\n");

    // Safe string concatenation
    const str1 = "Hello";
    const str2 = ", ";
    const str3 = "Zig!";

    const result = try std.mem.concat(allocator, u8, &[_][]const u8{ str1, str2, str3 });
    defer allocator.free(result);

    std.debug.print("Concatenated: {s}\n", .{result});

    // Safe string duplication
    const duplicated = try allocator.dupe(u8, result);
    defer allocator.free(duplicated);

    std.debug.print("Duplicated: {s}\n", .{duplicated});

    // Safe string comparison
    const are_equal = std.mem.eql(u8, result, duplicated);
    std.debug.print("Strings equal: {}\n\n", .{are_equal});
}

// 7. Working with slices
pub fn sliceOperations(allocator: std.mem.Allocator) !void {
    std.debug.print("=== Slice Operations ===\n");

    // Create a slice
    const original = try allocator.alloc(i32, 10);
    defer allocator.free(original);

    for (original, 0..) |*item, i| {
        item.* = @intCast(i * 10);
    }

    std.debug.print("Original slice: {any}\n", .{original});

    // Slice the slice
    const sub_slice = original[2..7];
    std.debug.print("Sub slice [2..7]: {any}\n", .{sub_slice});

    // Duplicate slice
    const duplicated = try allocator.dupe(i32, sub_slice);
    defer allocator.free(duplicated);

    std.debug.print("Duplicated sub slice: {any}\n", .{duplicated});

    // Resize slice
    const resized = try allocator.realloc(original, 15);
    defer allocator.free(resized);

    for (resized[10..]) |*item| {
        item.* = 999;
    }

    std.debug.print("Resized slice: {any}\n\n", .{resized});
}

// 8. Memory layout and struct packing
const PackedStruct = extern struct {
    a: u8,
    b: u32,
    c: u8,
};

const UnpackedStruct = struct {
    a: u8,
    b: u32,
    c: u8,
};

pub fn memoryLayoutDemo() void {
    std.debug.print("=== Memory Layout Demo ===\n");

    std.debug.print("Size of packed struct: {} bytes\n", .{@sizeOf(PackedStruct)});
    std.debug.print("Size of unpacked struct: {} bytes\n", .{@sizeOf(UnpackedStruct)});

    const packed = PackedStruct{ .a = 1, .b = 0x12345678, .c = 2 };
    const unpacked = UnpackedStruct{ .a = 1, .b = 0x12345678, .c = 2 };

    std.debug.print("Packed struct values: a={}, b=0x{x}, c={}\n", .{ packed.a, packed.b, packed.c });
    std.debug.print("Unpacked struct values: a={}, b=0x{x}, c={}\n\n", .{ unpacked.a, unpacked.b, unpacked.c });
}

// 9. COMTIME memory allocation
pub fn comptimeMemoryDemo() void {
    std.debug.print("=== Comptime Memory Demo ===\n");

    // Comptime allocation for compile-time constants
    const comptime_numbers = comptime blk: {
        var result: [10]usize = undefined;
        for (&result, 0..) |*item, i| {
            item.* = i * i;
        }
        break :blk result;
    };

    std.debug.print("Comptime squares: {any}\n", .{comptime_numbers});

    // Comptime string building
    const comptime_string = comptime std.fmt.comptimePrint("The answer is {}", .{42});
    std.debug.print("Comptime string: {s}\n\n", .{comptime_string});
}

// Main function demonstrating all memory management examples
pub fn main() !void {
    const gpa = std.heap.GeneralPurposeAllocator(.{}){};
    const allocator = gpa.allocator();

    std.debug.print("=== Zig Memory Management Examples ===\n\n");

    stackAllocation();

    try heapAllocation(allocator);

    try customAllocatorDemo();

    try arenaAllocatorDemo();

    try fixedBufferAllocatorDemo();

    try safeStringOperations(allocator);

    try sliceOperations(allocator);

    memoryLayoutDemo();

    comptimeMemoryDemo();

    std.debug.print("=== All Memory Examples Completed ===\n");

    // Check for memory leaks
    const leaked = gpa.detectLeaks();
    if (leaked) {
        std.debug.print("Memory leaks detected!\n");
    }
}

💻 Zig 并发编程 zig

🔴 complex ⭐⭐⭐⭐⭐

Zig并发模式,包括async/await、线程和通道

⏱️ 30 min 🏷️ zig, concurrency, async, threads, system
Prerequisites: Advanced Zig syntax and system programming
// Zig Concurrency and Async Examples

const std = @import("std");

// 1. Basic async/await
pub fn basicAsyncDemo() !void {
    std.debug.print("=== Basic Async Demo ===\n");

    // Async function that simulates work
    const asyncWork = struct {
        fn doWork(value: u32) u32 {
            std.time.sleep(100 * std.time.ns_per_ms);
            return value * 2;
        }
    }.doWork;

    // Call async function and await result
    const frame = try std.heap.page_allocator.create(@Frame(asyncWork));
    defer std.heap.page_allocator.destroy(frame);

    frame.* = async asyncWork(21);
    const result = await frame;

    std.debug.print("Async result: {}\n\n", .{result});
}

// 2. Multiple async operations
pub fn multipleAsyncDemo() !void {
    std.debug.print("=== Multiple Async Demo ===\n");

    const asyncTask = struct {
        fn computeSquare(n: u32) u32 {
            std.time.sleep(50 * std.time.ns_per_ms);
            return n * n;
        }

        fn computeCube(n: u32) u32 {
            std.time.sleep(75 * std.time.ns_per_ms);
            return n * n * n;
        }
    };

    // Create frames for async operations
    const square_frame = try std.heap.page_allocator.create(@Frame(asyncTask.computeSquare));
    const cube_frame = try std.heap.page_allocator.create(@Frame(asyncTask.computeCube));
    defer std.heap.page_allocator.destroy(square_frame);
    defer std.heap.page_allocator.destroy(cube_frame);

    // Start async operations
    square_frame.* = async asyncTask.computeSquare(5);
    cube_frame.* = async asyncTask.computeCube(3);

    // Wait for results
    const square_result = await square_frame;
    const cube_result = await cube_frame;

    std.debug.print("Square of 5: {}\n", .{square_result});
    std.debug.print("Cube of 3: {}\n\n", .{cube_result});
}

// 3. Thread-based concurrency
const WorkerTask = struct {
    id: u32,
    result: u32,

    fn compute(self: *WorkerTask) void {
        std.debug.print("Worker {} started\n", .{self.id});
        std.time.sleep(200 * std.time.ns_per_ms);
        self.result = self.id * 100;
        std.debug.print("Worker {} finished with result {}\n", .{self.id, self.result});
    }
};

pub fn threadDemo() !void {
    std.debug.print("=== Thread Demo ===\n");

    var workers: [4]WorkerTask = undefined;
    var threads: [4]std.Thread = undefined;

    // Create and start threads
    for (&workers, 0..) |*worker, i| {
        worker.* = WorkerTask{
            .id = @intCast(i + 1),
            .result = 0,
        };

        threads[i] = try std.Thread.spawn(.{}, WorkerTask.compute, .{worker});
    }

    // Wait for all threads to complete
    for (threads) |thread| {
        thread.join();
    }

    // Collect results
    var total: u32 = 0;
    for (workers) |worker| {
        total += worker.result;
    }

    std.debug.print("Total from all workers: {}\n\n", .{total});
}

// 4. Simple channel implementation
const Channel = struct {
    const Self = @This();
    const T = u32;

    buffer: [10]T,
    head: usize,
    tail: usize,
    count: usize,
    mutex: std.Thread.Mutex,
    condition: std.Thread.Condition,

    pub fn init() Self {
        return Self{
            .buffer = std.mem.zeroes([10]T),
            .head = 0,
            .tail = 0,
            .count = 0,
            .mutex = std.Thread.Mutex{},
            .condition = std.Thread.Condition{},
        };
    }

    pub fn send(self: *Self, value: T) void {
        self.mutex.lock();
        defer self.mutex.unlock();

        while (self.count == self.buffer.len) {
            self.condition.wait(&self.mutex);
        }

        self.buffer[self.tail] = value;
        self.tail = (self.tail + 1) % self.buffer.len;
        self.count += 1;
        self.condition.signal();
    }

    pub fn receive(self: *Self) T {
        self.mutex.lock();
        defer self.mutex.unlock();

        while (self.count == 0) {
            self.condition.wait(&self.mutex);
        }

        const value = self.buffer[self.head];
        self.head = (self.head + 1) % self.buffer.len;
        self.count -= 1;
        self.condition.signal();

        return value;
    }
};

const ProducerTask = struct {
    channel: *Channel,
    id: u32,

    fn run(self: *ProducerTask) void {
        for (0..5) |i| {
            const value = self.id * 10 + @as(u32, @intCast(i));
            std.debug.print("Producer {} sending {}\n", .{ self.id, value });
            self.channel.send(value);
            std.time.sleep(50 * std.time.ns_per_ms);
        }
    }
};

const ConsumerTask = struct {
    channel: *Channel,
    received: u32,

    fn init(channel: *Channel) ConsumerTask {
        return ConsumerTask{
            .channel = channel,
            .received = 0,
        };
    }

    fn run(self: *ConsumerTask) void {
        while (self.received < 10) {
            const value = self.channel.receive();
            std.debug.print("Consumer received {}\n", .{value});
            self.received += 1;
            std.time.sleep(30 * std.time.ns_per_ms);
        }
    }
};

pub fn channelDemo() !void {
    std.debug.print("=== Channel Demo ===\n");

    var channel = Channel.init();

    var producer1 = ProducerTask{ .channel = &channel, .id = 1 };
    var producer2 = ProducerTask{ .channel = &channel, .id = 2 };
    var consumer = ConsumerTask.init(&channel);

    // Create threads for producer-consumer pattern
    const producer1_thread = try std.Thread.spawn(.{}, ProducerTask.run, .{&producer1});
    const producer2_thread = try std.Thread.spawn(.{}, ProducerTask.run, .{&producer2});
    const consumer_thread = try std.Thread.spawn(.{}, ConsumerTask.run, .{&consumer});

    // Wait for all threads to complete
    producer1_thread.join();
    producer2_thread.join();
    consumer_thread.join();

    std.debug.print("Channel demo completed\n\n");
}

// 5. Async event loop simulation
const EventType = enum {
    timer,
    message,
    shutdown,
};

const Event = struct {
    type: EventType,
    data: u32,
};

const EventLoop = struct {
    const Self = @This();

    events: std.ArrayList(Event),
    running: bool,

    pub fn init(allocator: std.mem.Allocator) Self {
        return Self{
            .events = std.ArrayList(Event).init(allocator),
            .running = true,
        };
    }

    pub fn deinit(self: *Self) void {
        self.events.deinit();
    }

    pub fn addEvent(self: *Self, event: Event) !void {
        try self.events.append(event);
    }

    pub fn run(self: *Self) !void {
        std.debug.print("Event loop started\n");

        while (self.running) {
            if (self.events.items.len > 0) {
                const event = self.events.orderedRemove(0);

                switch (event.type) {
                    .timer => {
                        std.debug.print("Processing timer event with data {}\n", .{event.data});
                    },
                    .message => {
                        std.debug.print("Processing message event with data {}\n", .{event.data});
                    },
                    .shutdown => {
                        std.debug.print("Processing shutdown event\n");
                        self.running = false;
                    },
                }
            } else {
                std.time.sleep(10 * std.time.ns_per_ms);
            }
        }

        std.debug.print("Event loop stopped\n");
    }
};

pub fn eventLoopDemo() !void {
    std.debug.print("=== Event Loop Demo ===\n");

    const gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    var event_loop = EventLoop.init(allocator);
    defer event_loop.deinit();

    try event_loop.addEvent(Event{ .type = .message, .data = 1 });
    try event_loop.addEvent(Event{ .type = .timer, .data = 100 });
    try event_loop.addEvent(Event{ .type = .message, .data = 2 });
    try event_loop.addEvent(Event{ .type = .shutdown, .data = 0 });

    try event_loop.run();
    std.debug.print("\n");
}

// 6. Atomic operations for lock-free programming
pub fn atomicDemo() void {
    std.debug.print("=== Atomic Operations Demo ===\n");

    var counter: std.atomic.Value(u32) = std.atomic.Value(u32).init(0);

    // Increment atomically
    const old_value = counter.fetchAdd(1, .Monotonic);
    std.debug.print("Old value: {}, New value: {}\n", .{ old_value, counter.load(.Monotonic) });

    // Compare and swap
    const expected: u32 = 1;
    const new_value: u32 = 100;
    const swapped = counter.cmpxchgWeak(expected, new_value, .Monotonic, .Monotonic);

    if (swapped) {
        std.debug.print("Compare and swap succeeded\n");
    } else {
        std.debug.print("Compare and swap failed\n");
    }

    std.debug.print("Final value: {}\n\n", .{counter.load(.Monotonic)});
}

// 7. Coroutine-style generators
const NumberGenerator = struct {
    const Self = @This();

    current: u32,
    max: u32,

    pub fn init(max: u32) Self {
        return Self{
            .current = 0,
            .max = max,
        };
    }

    pub fn next(self: *Self) ?u32 {
        if (self.current >= self.max) return null;

        const result = self.current;
        self.current += 1;
        return result;
    }
};

pub fn generatorDemo() void {
    std.debug.print("=== Generator Demo ===\n");

    var generator = NumberGenerator.init(5);

    while (generator.next()) |number| {
        std.debug.print("Generated number: {}\n", .{number});
    }

    std.debug.print("Generator exhausted\n\n");
}

// Main function demonstrating all concurrency examples
pub fn main() !void {
    const gpa = std.heap.GeneralPurposeAllocator(.{}){};
    const allocator = gpa.allocator();
    defer _ = gpa.deinit();

    std.debug.print("=== Zig Concurrency and Async Examples ===\n\n");

    try basicAsyncDemo();

    try multipleAsyncDemo();

    try threadDemo();

    try channelDemo();

    try eventLoopDemo();

    atomicDemo();

    generatorDemo();

    std.debug.print("=== All Concurrency Examples Completed ===\n");
}