How to do allocations in structs

I’m trying to make a FIFO array (which I called a Queue, but not sure what the conventional name for it is). allocating memory for the items field, always causes OutOfMemory error.

src/main.zig:17:26: error: expected type 'main.Queue(u32)', found 'error{OutOfMemory}'
                .items = try allocator.alloc(T, capacity),
                         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/main.zig:6:12: note: struct declared here
    return struct {
           ^~~~~~
src/main.zig:14:56: note: function cannot return an error
        fn init(allocator: Allocator, capacity: usize) Self {

This is my code:

const std = @import("std");
const Allocator = std.mem.Allocator;
const GPA = std.heap.GeneralPurposeAllocator;

fn Queue(comptime T: type) type {
    return struct {
        const Self = @This();

        allocator: Allocator,
        items: []T,
        len: usize,
        capacity: usize,

        fn init(allocator: Allocator, capacity: usize) Self {
            return .{
                .allocator = allocator,
                .items = try allocator.alloc(T, capacity),
                .len = 0,
                .capacity = capacity,
            };
        }

        fn deinit(self: Self) Self {
            self.allocator.free(self.items);
        }
    };
}

pub fn main() !void {
    var gpa = GPA(.{}){};
    const allocator = gpa.allocator();
    defer _ = gpa.deinit();

    const q = Queue(u32).init(allocator, 8);
    defer q.deinit();
}

Obviously I know that changing the return type to !Self gets rid of the error, but my question is why is it always OutOfMemory?

Because std.mem.Allocator.alloc() is defined to return only OutOfMemory:

const std = @import("../std.zig");
const assert = std.debug.assert;
const math = std.math;
const mem = std.mem;
const Allocator = @This();
const builtin = @import("builtin");

pub const Error = error{OutOfMemory};

// ...

pub fn alloc(self: Allocator, comptime T: type, n: usize) Error![]T {
    return self.allocAdvancedWithRetAddr(T, null, n, @returnAddress());
}

https://ziglang.org/documentation/master/std/#src/std/mem/Allocator.zig

Hey @weaea, can you rephrase your question a bit? There are two ways I can interpret what you are asking:

  1. Why does allocator only return OutOfMemory as an error?
  2. Why do I need to put the ! in the return type?

Essentially, as @chung-leong pointed out, you have to have the ! in the return type because when something can return an error, you either need to use catch in your code to prevent it from passing through the function back to the caller or add a !Self or MyErrorType!Self to allow for an error union to be returned from your function.

2 Likes

To clarify, OutOfMemory is the possible error that alloc can return. Its not saying that the Allocator is out of memory, just that it is possible for that to be the case.
It’s not causing an OutOfMemory error, it is saying that is a possible outcome.
As @AndrewCodeDev pointed out, you will need to eiter update the init function to also say that it can return an error like !Self, or do a catch on the alloc call.

3 Likes

I’m sorry, I didn’t think I worded it badly.
What I’m trying to do is to allocate memory to a field in the struct, but it always fails with OutOfMemory error, so my question is how do correctly I allocate memory to a field in a struct?

The reason I’m confused about this behavior is that I’m essentially doing the same thing as in the code below, except that the code below works.

pub fn main() !void {
    var gpa = GPA(.{}){};
    const allocator = gpa.allocator();
    defer _ = gpa.deinit();

    const a = try allocator.alloc(u32, 32);
    defer allocator.free(a);
}

The reason I emphasized the OutOfMemory error, is because my machine is definitely not out of memory, so what’s going on?

I didn’t see this reply before replying to @AndrewCodeDev. this solved my problem.
What happened is that I thought since I’m using the try keyword when allocating, that means the allocation doesn’t return the error anymore.
Turns out my understanding of the try keyword was incorrect.

Thanks to everyone <3

2 Likes