How to return String with in ArrayList that gets freeed?

Hello,
I am trying to get the work the following, but no matte what I try I get an Segmentation fault.

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

pub fn get(allocator: Allocator, input: []const u8) ![]const []const u8 {
    const joined: []const u8 = try std.mem.join(allocator, "", &[_][]const u8{ "a", input, "c" });
    defer allocator.free(joined);
    var result = std.ArrayList([]const u8).init(allocator);
    try result.append(joined);
    try result.append("d");
    return try result.toOwnedSlice();
}

const testing = std.testing;

test "trimming" {
    const allocator = testing.allocator;

    const result = try get(allocator, "b");
    defer allocator.free(result);

    try testing.expectEqualStrings("abc", result[0]);
    try testing.expectEqualStrings("d", result[1]);
}

I am trying to return the joined String in an array and make it so that the caller only has to free the result.
Any ideas or help is appreciated.

result[0] points to freed memory.
Because joined is freed by defer allocator.free(joined); when get function ends, and a reference to joined is appended at result.

Exactly, but then I need to free the result[0] separately.

defer {
        allocator.free(result[0]);
        allocator.free(result);
}

Is it possible to only have to free the result?
Do I have to clone the result?

If the result is a slice that contains dynamically allocated pointers, those have to be individually freed. There’s no real way around that, unless you do know those have been allocated inside an arena.

Much like you have an allocation function (get()) you can have a deallocation function (destroy() or similar) to which you can pass slice and allocator to do the deallocation in one step. This makes it easier for the caller to manage memory correctly:

const x = get(allocator, ...);
defer destroy(allocator, x);

I think what you’re looking for is either to accept arena, and “lift” the whole constraint to the upper level, or return a struct with a newly created arena, and in that case, you’d probably dupe() the input first.

Arenas are often used to denote how long certain things will “live” together.

Thanks, I understood it now and went with duping the input.