Stream: beginners

Topic: ✔ understanding RocListWith(u8, false)


view this post on Zulip removewingman (Oct 19 2026 at 18:23):

@Anton Yes.

view this post on Zulip Anton (Oct 20 2026 at 10:27):

I ran some quick analysis with Claude but did not run the code, please let me know if the suggestions here work for you.

The list from file_content_bytes_absolute! is fine. The leak is in hostedStdoutWriteBytes in src/stdstreams.zig. Roc passes byte_content to Stdout.write_bytes!, and that hosted function never releases it. The stack trace points at fromSlice only because that's where the leaked block was allocated.

Hosted functions own their refcounted arguments and must release them before returning. The generated roc_platform_abi.zig says this at the top, and also in the doc comment for roc_stdout_write_bytes:

/// Owned arguments. Release each exactly once before returning, ...
///     arg0.decref(roc_host);

Your other stdout/stderr functions already do defer str.decref(roc_host), so this one just needs the same line:

pub fn hostedStdoutWriteBytes(bytes: RocListWith(u8, false)) callconv(.c) StdoutResult {
    const roc_host = g_roc_host.?;
    defer bytes.decref(roc_host);
    ...

You don't need to configure anything on the platform side, and no decref is needed after roc_main. If a hosted function returns or stores an argument instead, it passes ownership on rather than releasing it.

A few other things Claude noticed while reading:

view this post on Zulip removewingman (Oct 20 2026 at 11:08):

This helped, thank you, much appreciated . It was actually the bug, it did not show up earlier because before I only passed static Roc.Str into the function, makes sense. :heart_hands:

view this post on Zulip Notification Bot (Oct 20 2026 at 11:08):

removewingman has marked this topic as resolved.


Last updated: Nov 04 2026 at 18:54 UTC