Zig version: 0.17
Hi there, I am learning Zig and my first serious project is to implement a small app that gets a payload from the job queue and posts it to Slack API. The API works with the concept of “blocks”, each block can represent text, image, or more complex composite types. Blocks can be nicely represented with tagged unions. But I have to deal with somewhat generic structs: Parent and Child.
However, I am stuck with the simplest task - initializing a struct that can be properly serialized. With the current implementation I am getting a Segmentation fault.
I have reduced my code to this simplest failing snippet.
const std = @import("std");
const Child = struct {
text: []const u8,
};
const Parent = struct {
type: []const u8,
elements: []const Child,
};
const Msg = struct {
elements: []const Parent,
pub fn init(v: []const u8) Msg {
const children = &[_]Child{.{
.text = v,
}};
const parent: Parent = .{
.type = "parent-type",
.elements = children,
};
return Msg{
.elements = &[_]Parent{parent},
};
}
};
pub fn main(init: std.process.Init) !void {
const allocator: std.mem.Allocator = init.arena.allocator();
const msg = Msg.init("test-value");
const out = try std.json.Stringify.valueAlloc(allocator, msg, .{});
defer allocator.free(out);
std.debug.print("JSON: {s}\n", .{out});
}
Output:
Segmentation fault at address 0x0
/usr/local/lib/zig/std/unicode.zig:243:43: 0xea12f86edb8 in utf8ValidateSliceImpl__anon_33805 (main)
const chunk: Chunk = remaining[0..chunk_len].*;
^
/usr/local/lib/zig/std/unicode.zig:232:33: 0xea12f86eca4 in utf8ValidateSlice (main)
return utf8ValidateSliceImpl(input, .cannot_encode_surrogate_half);
^
/usr/local/lib/zig/std/json/Stringify.zig:506:95: 0xea12f86bde2 in write__anon_33493 (main)
if (!self.options.emit_strings_as_arrays and std.unicode.utf8ValidateSlice(slice)) {
^
/usr/local/lib/zig/std/json/Stringify.zig:477:35: 0xea12f86b088 in write__anon_33471 (main)
try self.write(@field(v, Field.name));
^
/usr/local/lib/zig/std/json/Stringify.zig:513:35: 0xea12f86ad9a in write__anon_33434 (main)
try self.write(x);
^
/usr/local/lib/zig/std/json/Stringify.zig:477:35: 0xea12f86a7c2 in write__anon_33380 (main)
try self.write(@field(v, Field.name));
^
/usr/local/lib/zig/std/json/Stringify.zig:575:16: 0xea12f86a5d4 in value__anon_33336 (main)
try s.write(v);
^
/usr/local/lib/zig/std/json/Stringify.zig:621:10: 0xea12f86a37b in valueAlloc__anon_30459 (main)
value(v, options, &aw.writer) catch return error.OutOfMemory;
^
/home/me/projects/repr/src/main.zig:36:50: 0xea12f8659f0 in main (main)
const out = try std.json.Stringify.valueAlloc(allocator, msg, .{});
^
/usr/local/lib/zig/std/start.zig:737:30: 0xea12f866170 in callMain (main)
return wrapMain(root.main(.{
^
???:?:?: 0xea12f74e53a in ??? (/home/me/.cache/zig/o/1c066c708c3cff05d1d3c930e4312205/main)
Abort trap (core dumped)
I want to use the init function, because in practice a Slack message will contain many blocks with multiple default field values and I need a concise way to initialize them.
If I go the explicit way, then I get the expected JSON:
pub fn main(init: std.process.Init) !void {
const allocator: std.mem.Allocator = init.arena.allocator();
//const msg = Msg.init("test-value");
const msg: Msg = .{
.elements = &[_]Parent{.{
.type = "parent-type",
.elements = &[_]Child{.{ .text = "expected-text" }},
}},
};
const out = try std.json.Stringify.valueAlloc(allocator, msg, .{});
defer allocator.free(out);
std.debug.print("JSON: {s}\n", .{out});
}
Output:
$ zig run main.zig
JSON: {“elements”:[{“type”:“parent-type”,“elements”:[{“text”:“expected-text”}]}]}
I would appreciate if you can point me in a direction where I can read more about it.