#Freeing ArrayList data

1 messages · Page 1 of 1 (latest)

fallow pollen
#

You are not calling deinit on l

#

You are only freeing the contents

faint void
#

need to also free the individual strings after toOwnedSlice, which moves the items from the original list into the new slice (which is safe to do, since toOwnedSlice empties the original list, making the first deinit and free loop a noop)

fallow pollen
#

When using a debugallocator you can see exactly which allocation leaked, which double freed, etc

faint void
#

yeah, that means you aren't freeing everything

#

you need to free everything at every exit point

#

defer inside a function doesn't propagate outside the function

#

defers always execute at scope exit

#

so you need to free those string elements in a loop wherever you're returning it to

#

gpa.deinit doesn't free your memory

#

it just tells you that you forgot to free your memory

fallow pollen
#

If you are looking for a cyclical allocation deallocation pattern, like a game loop or a Web server http request, you'd be better off using an arena allocator, but you should get the basics of memory allocation first yeah

faint void
#

the allocator contract specifies that you should call free on memory you are no longer using. allocators depend on this to be able to re-use memory throughout the runtime of the program, instead of using infinitely more memory until you run out. allocators like debugallocator tell you that you forget to free memory

#

l.items is empty when you run that loop at the end

#

like I said, toOwnedSlice makes it empty

#

you have to free them from os

#

defer for (os) |str| gpa.free(str);

#

since the first defer for (l.items) |str| gpa.free(str); becomes a noop, you have to free the strings where they actually are

#

are you familiar with RAII and/or move semantics from C++, Rust, or any other language?

#

if not, I do second @fallow pollen's sentiment in trying to understand ownership and lifetimes on a more conceptual level

fallow pollen
#

Yeah arenas just abstract it away way too much to the point where you start throwing them everywhere

#

In 0.15,this is the source code of toOwnedSlice

pub fn toOwnedSlice(self: *Self, gpa: Allocator) Allocator.Error!Slice {
    const old_memory = self.allocatedSlice();
    if (gpa.remap(old_memory, self.items.len)) |new_items| {
        self.* = .empty;
        return new_items;
    }

    const new_memory = try gpa.alignedAlloc(T, alignment, self.items.len);
    @memcpy(new_memory, self.items);
    self.clearAndFree(gpa);
    return new_memory;
}

The clearAndFree call sets the items.len to 0, meaning the l for loop won't iterate through any items as the ownership of the items is passed to the owned slice with @memcpy.

The approach is what ink said, freeing the items from the new slice.

It's also best practice if you add a errdefer which frees the items from l in case you get any errors before you manage to call toOwnedSlice, such as an OOM in allocPrint. It's not necessary to do, but if you expect this to error out at some point without the error exiting the entire program, then you'll leak