Как корректно передать данные из std.ArrayList в Slice?
Столкнулся со странным поведением структуры в zig. Точнее слайса ([]const u8) являющегося одним из элементов структуры, ниже пример.
const Statement = struct {
content: []const u8,
};
// read file
var script_file = try std.fs.cwd().openFile("text.txt", .{});
defer script_file.close();
// allocator
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
// Read the text file by line
var buf_reader = std.io.bufferedReader(script_file.reader());
const reader = buf_reader.reader();
var line = std.ArrayList(u8).init(allocator);
defer line.deinit();
var line_no: usize = 0;
var statements: [26]Statement = undefined; // в файле фиксированное количество строк
while (reader.streamUntilDelimiter(line.writer(), '\n', null)) : (line_no += 1) {
defer line.clearRetainingCapacity();
var i: u16 = 0;
// write content in statments
for (line.items) |char| {
if (char == ' ') {
statements[line_no] = .{ .content = line.items[(i + 1)..] };
break;
}
i += 1;
}
std.debug.print("State content -- {d}:\t {s}\n", .{ line_no, statements[line_no].content }); // тут statements[line_no].content выводит корректно
} else |err| switch (err) {
error.EndOfStream => {}, // Continue on
else => std.debug.print("Ошибка при построчном чтении файла... {any}", .{err}), // Print error
}
inline for (statements) |s| {
std.debug.print("State content len: {d}\n", .{s.content.len}); // корректно печатает длину слайса
std.debug.print("State content ptr: {any}\n", .{s.content.ptr}); // печатает указатель на слайс
std.debug.print("State content: {s}\n", .{s.content}); // здесь происходит ошибка:
// State content: thread 13433 panic: reached unreachable code
}
Т.е. компиляция проходит успешно, но обратиться к элементу структуры вне цыкла в котором были перенесины данные не получается. При том длину строки отображает корректно. Возможно кто-то сталкивался с таким? Версия zig - 0.12.0-dev.2341+92211135f.
Ответы (1 шт):
Тут проблема в том, что вы в структуры записываете указатель на один и тот же буфер из line. При отладочном выводе в конце видно, что content.ptr один и тот же (тестировалось на релизных версиях 0.12.0, 0.13.0, panic не было):
State content len: 9
State content ptr: u8@72a84e679005
State content: artrthrth
State content len: 7
State content ptr: u8@72a84e679005
State content: artrthr
State content len: 4
State content ptr: u8@72a84e679005
State content: artr
State content len: 14
State content ptr: u8@72a84e679005
State content: artrthrth5rth4
State content len: 10
State content ptr: u8@72a84e679005
State content: artrthrth5
Входные данные были такие:
test aasdfwef1
test ergerh2
test tjy3
test aweerhrethrth4
test artrthrth5
Видно, что State content выше отображает какой-то мусор (точнее, не какой-то мусор, а вполне конкретный: то, что последнее было записано в буфер line, но с длиной как у фактически записанной строки, т.к. слайс хранит адрес и длину, вот длина как раз правильная, но адрес один и тот же).
Ну хотя если бы в моих данных перой частью был не test везде, а слова разной длины, то адрес был бы разный, но отличающийся на единицы байт.
Чтобы работало нормально, нужно на каждую строку аллоцировать новый буфер, в него копировать нужную часть строки:
// Уменьшил количество строк до 5, чтобы не набивать 26 рандомных строк файла
var statements: [5]Statement = undefined;
// Считаем, что при выходе из программы все строки заполнены корректными данными,
// и в content лежат валидные указатели, которые нужно будет освободить
defer {
for (statements) |s| {
allocator.free(s.content);
}
}
while (reader.streamUntilDelimiter(line.writer(), '\n', null)) : (line_no += 1) {
defer line.clearRetainingCapacity();
var i: u16 = 0;
// write content in statments
for (line.items) |char| {
if (char == ' ') {
// Аллоцируем память и копируем в нее данные из нужного куска line.items
const content = try allocator.dupe(u8, line.items[i + 1 ..]);
// Записываем указатель на буфер в структуру
statements[line_no] = .{ .content = content };
break;
}
i += 1;
}
std.debug.print("State content -- {d}:\t {s}\n", .{ line_no, statements[line_no].content });
} else |err| switch (err) {
error.EndOfStream => {}, // Continue on
else => std.debug.print("Ошибка при построчном чтении файла... {any}", .{err}), // Print error
}
Входной файл:
test aasdfwef1
test ergerh2
test tjy3
test aweerhrethrth4
test artrthrth5
Вывод:
State content -- 0: aasdfwef1
State content -- 1: ergerh2
State content -- 2: tjy3
State content -- 3: aweerhrethrth4
State content -- 4: artrthrth5
State content len: 9
State content ptr: u8@75d2defd8000
State content: aasdfwef1
State content len: 7
State content ptr: u8@75d2defe0000
State content: ergerh2
State content len: 4
State content ptr: u8@75d2defee000
State content: tjy3
State content len: 14
State content ptr: u8@75d2defd8010
State content: aweerhrethrth4
State content len: 10
State content ptr: u8@75d2defd8020
State content: artrthrth5