Handling error types when using while loop with iterators

Hey everyone! I’m a bit stuck with this one, and I sincerely hope that someone will shed some light on where exactly I’m wrong.

Basically, I’m trying to write a function that should break up a given string into an array of 4 u8 integers (IPv4 address parsing). It goes like this -

const std = @import("std");

pub const NetworkAddressError = error {
    IPV4ParseError,
};

pub fn ipV4StringToSlice(ip_addr: []const u8) ![4]u8 {
    const heap = std.heap.page_allocator;
    const buffer = try heap.alloc(u8, 32);
    defer heap.free(buffer);

    var fba = std.heap.FixedBufferAllocator.init(buffer);
    const alloc = fba.allocator();

    // Using ArrayList because we do not know the values at the compile time;
    // We get them from elsewhere (i.e. CLI args or environment variables)
    var address = try std.ArrayList(u8).initCapacity(alloc, 4);
    defer address.deinit(alloc);

    var iter = std.mem.splitScalar(u8, ip_addr, '.');

    while(iter.next()) |val| {
        const octet = try std.fmt.parseInt(u8, val, 10);
        if (address.append(alloc, octet)) |_| {
            return address.items;
        } else |err| {
            std.debug.print("Error: invalid octet number;\n{any}\n", .{ err });
            return NetworkAddressError.IPV4ParseError;
        }
    }
}

Unfortunately no matter what I try, I get the following compilation error - error: expected type '@typeInfo(@typeInfo(@TypeOf(misc.ipV4StringToSlice)).@"fn".return_type.?).error_union.error_set![4]u8', found '[]u8'. I have tried handling error in all the possible ways I could imagine, but to zero success.

So my question is - what is the correct way of handling errors in these particular situations (using while loops with iterators)? Is it possible at all or my function logic is flawed at root? I would be immensely grateful for any insight you might share!

The error is, that the function wants to return a [4]u8 (this is an array, which length is known at comptime to be 4), but you are returning a []u8 (which stores both a pointer to an array as well as the length of it as a runtime variable) in return address.items;. If you know the length of the array at comptime, you can do var address: [4]u8 = undefined;. Then you can just return address.

2 Likes

@rpkak explained the compile error

Your code is also overcomplicated

Instead of an fba and array list, you can just:

var ip: [4]u8 = undefined;
var i: usize = 0;
//...
while (it.next()) |val| : (i += 1) {
  //...
  if (i == ip.len) break;
  ip[i] = octet;
}

return ip

While loop docs if you haven’t seen this syntax before.

This also solves the compile error.

an fba is more useful when you have more complex data that you want to dynamically allocate with a fixed maximum amount of memory.

Also for future reference you can create a pointer to an array from a slice, by using compile time known start and end

const arr_ptr: *[4]u8 = slice[0..4];
// which you can dereference to solve your compile error
return arr_ptr.*;
1 Like

Thank you, this worked like a charm!

I guess I need to do some extra work in understanding memory management and what can be used in the runtime and what is compile time exclusively. This is my first experience doing low-level code, so I guess shooting myself in the foot is inevitable ( ._.)

Thanks again for your explanation and have a great day!