I was recently debugging a segfault that I was able to trace back to my construction of a struct with deeply nested slices, in the end I needed to add a dupe:
Why is this dupe required (sorry it is a wall of code)?
Is there a way for me to more easily avoid problems like this?
I am constructing a deeply nested struct with a nested slices using some for loops, ArrayList, and toOwnedSlice(). The function is part of a larger memory allocation strategy that uses an arena, which is why leaking is ok (and why the function is called Leaky and accepts an arena).
pub fn initFromENILeaky(arena: std.mem.Allocator, eni: gcat.ENI, options: Options) error{OutOfMemory}!Zenoh {
var subdevices: std.ArrayList(Zenoh.ENI.Subdevice) = .empty;
for (eni.subdevices, 0..) |eni_subdevice, subdevice_index| {
var inputs: std.ArrayList(Zenoh.ENI.Subdevice.PDO) = .empty;
for (eni_subdevice.inputs) |eni_input| {
var entries: std.ArrayList(Zenoh.ENI.Subdevice.PDO.Entry) = .empty;
for (eni_input.entries) |eni_entry| {
if (eni_entry.isGap()) continue;
const substitutions: ProcessVariableSubstitutions = .{
.subdevice_index = zenohSanitize(try std.fmt.allocPrint(arena, "{}", .{subdevice_index})),
.subdevice_name = zenohSanitize(try std.fmt.allocPrint(arena, "{?s}", .{eni_subdevice.name})),
.pdo_direction = "input",
.pdo_name = zenohSanitize(try std.fmt.allocPrint(arena, "{?s}", .{eni_input.name})),
.pdo_index_hex = zenohSanitize(try std.fmt.allocPrint(arena, "{x}", .{eni_input.index})),
.pdo_entry_index_hex = zenohSanitize(try std.fmt.allocPrint(arena, "{x}", .{eni_entry.index})),
.pdo_entry_subindex_hex = zenohSanitize(try std.fmt.allocPrint(arena, "{x}", .{eni_entry.subindex})),
.pdo_entry_description = zenohSanitize(try std.fmt.allocPrint(arena, "{?s}", .{eni_entry.description})),
};
try entries.append(
arena,
Zenoh.ENI.Subdevice.PDO.Entry{
.index = eni_entry.index,
.subindex = eni_entry.subindex,
//////////////////////////////////////////// DUPE HERE ////////////////////////////////////////////////////
.publishers = if (options.pdo_input_publisher_key_format) |pdo_input_publisher_key_format| try arena.dupe(
Zenoh.ENI.PubSub,
&.{
.{
.key_expr = try processVaribleNameSentinelLeaky(arena, pdo_input_publisher_key_format, substitutions, 0),
},
},
) else &.{},
.subscribers = &.{},
},
);
}
try inputs.append(arena, Zenoh.ENI.Subdevice.PDO{
.index = eni_input.index,
.entries = try entries.toOwnedSlice(arena),
});
}
var outputs: std.ArrayList(Zenoh.ENI.Subdevice.PDO) = .empty;
for (eni_subdevice.outputs) |eni_output| {
var entries: std.ArrayList(Zenoh.ENI.Subdevice.PDO.Entry) = .empty;
for (eni_output.entries) |eni_entry| {
if (eni_entry.isGap()) continue;
const substitutions: ProcessVariableSubstitutions = .{
.subdevice_index = zenohSanitize(try std.fmt.allocPrint(arena, "{}", .{subdevice_index})),
.subdevice_name = zenohSanitize(try std.fmt.allocPrint(arena, "{?s}", .{eni_subdevice.name})),
.pdo_direction = "output",
.pdo_name = zenohSanitize(try std.fmt.allocPrint(arena, "{?s}", .{eni_output.name})),
.pdo_index_hex = zenohSanitize(try std.fmt.allocPrint(arena, "{x}", .{eni_output.index})),
.pdo_entry_index_hex = zenohSanitize(try std.fmt.allocPrint(arena, "{x}", .{eni_entry.index})),
.pdo_entry_subindex_hex = zenohSanitize(try std.fmt.allocPrint(arena, "{x}", .{eni_entry.subindex})),
.pdo_entry_description = zenohSanitize(try std.fmt.allocPrint(arena, "{?s}", .{eni_entry.description})),
};
try entries.append(arena, Zenoh.ENI.Subdevice.PDO.Entry{
.index = eni_entry.index,
.subindex = eni_entry.subindex,
.publishers = if (options.pdo_output_publisher_key_format) |pdo_output_publisher_key_format| try arena.dupe(
Zenoh.ENI.PubSub,
&.{
.{
.key_expr = try processVaribleNameSentinelLeaky(arena, pdo_output_publisher_key_format, substitutions, 0),
},
},
) else &.{},
.subscribers = if (options.pdo_output_subscriber_key_format) |pdo_output_subscriber_key_format| try arena.dupe(
Zenoh.ENI.PubSub,
&.{
.{
.key_expr = try processVaribleNameSentinelLeaky(arena, pdo_output_subscriber_key_format, substitutions, 0),
},
},
) else &.{},
});
}
try outputs.append(arena, Zenoh.ENI.Subdevice.PDO{
.index = eni_output.index,
.entries = try entries.toOwnedSlice(arena),
});
}
try subdevices.append(arena, Zenoh.ENI.Subdevice{
.inputs = try inputs.toOwnedSlice(arena),
.outputs = try outputs.toOwnedSlice(arena),
});
}
return Zenoh{ .eni = .{ .subdevices = try subdevices.toOwnedSlice(arena) } };
}