Search code examples
arrayscastingzig

expected type 'i32', found 'usize'


I'm currently trying to solve some leetcode problems in Zig, and I'm doing the two sum problem. Here's what the entirety of my code looks like:

const std = @import("std");
const allocator = std.heap.page_allocator;

fn two_sum(nums: []i32, target: i32) []i32{
    var map = std.AutoArrayHashMap(i32, i32).init(allocator);
    defer map.deinit();
    var res = [2]i32{-1, -1};
    for (nums, 0..) |n, i| { // n is the number; i is the index
        if (map.get(target - n)) |v| {
            res[0] = v;
            res[1] = @as(i32, i);
            return &res;
        }
        try map.put(n, @as(i32, i));
    }
    return &res;
}

pub fn main() !void {
    var arr = [_]i32{1, 5, 8, 9, 6};
    var x = two_sum(&arr, 9);
    for (x) |n| {
        std.debug.print("{d}", .{n});
    }
}

However, when I run the code, I get this error:

error: expected type 'i32', found 'usize'
            res[1] = @as(i32, i);
                ^

Why is zig interpreting 1 as a usize and not an i32? And what can be done to fix this?

I tried using an explicit type cast: res[@as(i32, 1)] = @as(i32, i);

However, that didn't work either.


Solution

  • The immediate problem is that i has type usize, and you can't cast from usize to i32 using @as since an i32 can't hold all of the values that a usize might hold. Instead, use @intCast which is specifically for these situations when you know that the cast is safe. There are two such casts using @as that need to be changed to @intCast.

    The two_sum function needs to return an error union since it is using try around the call to map.put. And since two_sum needs to return an error union, calls to two_sum need to handle that, e.g., by using try.

    Finally, res is a local variable, and you shouldn't return a pointer to a local variable; the storage associated with local variables is not valid after control leaves the local scope, and attempting to access such invalid memory leads to undefined behavior. Instead of returning a pointer, just return (a copy of) the array.

    Here is a version of OP posted code with the above changes.

    const std = @import("std");
    const allocator = std.heap.page_allocator;
    
    fn two_sum(nums: []i32, target: i32) ![2]i32 {  // return error union
        var map = std.AutoArrayHashMap(i32, i32).init(allocator);
        defer map.deinit();
        var res = [2]i32{ -1, -1 };
        for (nums, 0..) |n, i| {
            if (map.get(target - n)) |v| {
                res[0] = v;
                res[1] = @intCast(i32, i);     // use `@intCast` instead of `@as`
                return res;                    // just return the array
            }
            try map.put(n, @intCast(i32, i));  // use `@intCast` instead of `@as`
        }
        return res;                            // just return the array
    }
    
    pub fn main() !void {
        var arr = [_]i32{1, 5, 8, 9, 6};
        var x = try two_sum(&arr, 9);  // `two_sum` returns an error union
        for (x) |n| {
            std.debug.print("{d}", .{n});
        }
        std.debug.print("\n", .{});
    }