#panic: reached unreachable code --> yet unclear where in my code caused this

1 messages Β· Page 1 of 1 (latest)

void orbit
#

Full error attached.

I can include code also but essentially I've had to start placing breakpoints and tracking this down the old-fashion way πŸ˜‰

I suspect this has to do with how I'm formatting the config here (see screenshot)

Its a bit of a mess now because I took what was a single line print and broke it up hoping to help the compiler hint at which line was actually leading to this issue.

grand gorge
void orbit
#

You mean the latest zig compiler? that is what i'm using now

#

compiled from source

grand gorge
#

Then what's this part of the path? πŸ€” (where it says 0.13.0)
..../linux-x86_64-0.13.0/lib/std/posix.zig

void orbit
#

great question:

#

ah you know what, I wonder if this is because of running the test with the zig Lanugage extension that only supports 0.13.0

#

i'll do a one off from a terminal

grand gorge
#

That makes more sense. Anyway std code shouldn't reach unreachable so, you know, sounds like a bug.

void orbit
#

πŸ₯³ yayyy bug

rocky mural
#

Passing bad buffer is a programmer error.

void orbit
#

ouch

#

lol

grand gorge
#

Damn

rocky mural
#

So if you can guarantee that's not the case, then it may be bug.

#

EFAULT buf is outside your accessible address space.

wise sundial
#

youre deiniting the envmap so the memory is freed

rocky mural
#

^ there you go

grand gorge
#

Ah I miseed these lines before

/home/neo/repos/crumb/src/utils/config.zig:90:24: 0x104958f in displayConfig (test)
        std.debug.print("HOST: {s}\n", .{self.host});
                       ^
/home/neo/repos/crumb/src/utils/config.zig:102:25: 0x1047888 in test.load .env file (test)
    config.displayConfig();
void orbit
#

Ok I guess its not clear to me when using defer there, when its actually freed.

wise sundial
#

at the end of the scope its in, in that case the function

void orbit
#

Alright by that time should the values not be stored in the Config obj being returned to the caller

where then "config" var in test owns that memory location?

rocky mural
#

After return, you hold onto e.g. host string. You can keep the defer if you copy that and other buffers, then you'd need to free them after the test. Alternatively, you can hold to dotenv in config and free it after the test.

void orbit
#

I don't think I have a clear understanding of how to work with allocators here or something becuase I don't quite understand what you mean.

rocky mural
#

Currenly code work as follows:

  • you read dotenv file and allocate structure in env_map
  • you do some transformations on it, storing the results in config
  • you dealloc the env_map

If you have a pointer to any data inside env_map after function returns, it becomes invalid and crashes.

void orbit
#

my understanding is that I'm passing the allocator to dotenv.init which is then giving me back the env_map

then I'm "get"ing the values, storing those in a new memory location per var (host for ex)

after which I'm giving ownership to the Config owner by returning it to the caller

at which point, env_map goes out of scope and is dealloced

so this is really a bunch of ptr mov instead of copies and I should either decide to copy the values in the buffer to the config (not 100% on how to do this), or decide to hold onto the buffer mem until when exactly?

rocky mural
#

If you look at host variable, you get it by env_map.get("HOST"). You get a slice as a string to it, but it's still owned by env_map. To keep it in your object, you need a copy, so allocator.dupe()

#

so this is really a bunch of ptr mov instead of copies and I should either decide to copy the values in the buffer to the config (not 100% on how to do this), or decide to hold onto the buffer mem until when exactly?

You need to ensure the buffer exists when you use it. Either by ensuring that owner of a buffer env_map exists at that point or by copying and tying the buffer to your Config object.

grand gorge
void orbit
#

ok did not know about .dupe()

the goal here is really to only hold onto the env_map for a very short time and drop all mem after values are stored in the new Config location

#

maybe put better, "after just the values are copied into the Config obj I don't need to hold onto the much larger buffers of teh whole env_map anymore"

rocky mural
#

Yup, makes sense. Maybe implement getCopy/putCopy on dotenv if you need it.

void orbit
void orbit
#

or just point me to any example really and I can figure it out

rocky mural
#

Just additional allocator.dupe(), it may be more readable there than in callsite but make a decision yourself.

grand gorge
#

Don't forget your errdefers. Need that with multiple allocations unless you use an arena

void orbit
#

did I mention I just picked up this lang last night πŸ€” haha

alright thanks for pointing these things out, I'll play around for a while and see where I land with this

#

tyvm πŸ™

grand gorge
# void orbit did I mention I just picked up this lang last night πŸ€” haha alright thanks for ...

Something like this:

[...]
        // Would need to free those later...
        const host_owned = try allocator.dupe(u8, host);
        errdefer allocator.free(host_owned);

        const proto_file_owned = try allocator.dupe(u8, proto_file);
        errdefer allocator.free(proto_file_owned);

        const pem_file_owned = try allocator.dupe(u8, pem_file);

        return Config{
            .host = host_owned,
            .port = parsed_port,
            .compression_type = parsed_compression_type,
            .reliable = parsed_reliable,
            .proto_file = proto_file_owned,
            .pem_file = pem_file_owned,
        };
#

You don't actually need to dupe in case you use your defaults. But then you'd need to keep track when defaults where used and when not in order to know what to free. And that's a bit messy.

#

Anyway this is example is definetely not the best way to do this

#

But it's a way

void orbit
#

Right and I'm guessing that I don't need to dupe in the areas where I "parsed" to CompressionType and to bool for my "parsed_*" values

grand gorge
#

Yes you don't need for parsed because you are not using the strings anymore

void orbit
#

I was playing with getCopy:

pub fn getCopy(self: Self, key: []const u8, allocator: Allocator) !?[]u8 {
if (self.map.get(key)) |value| {
var copy = try allocator.alloc(u8, value.len);
@memcpy(copy, value);
return copy;
}
return null;
}

#

but then I still need to free those at some point regardless

rocky mural
#

Yup, probably may be smart to create Config.deinit

void orbit
#

is there shorthand for "keep values as long as Config lives"

#

ah ok

#

just make it explicit then with its own deinit

grand gorge
#

I would just use an arena allocator (look into that).

void orbit
#

will do

#

after professionally working with rust for the past 3.5 years this is quote a change in thinking tbh haha

grand gorge
#

You could have an arena that allocates just for the lifetime that you need those env variables and deinit after

void orbit
#

but a welcome change if it means I can get away from "lifetime coloring" πŸ™‚

grand gorge
#

The arena frees everything for you πŸ™‚

rocky mural
#

Never wrote in Rust but seems like you're in better position than most people starting in Zig due to understanding ownership

void orbit
#

the whole project is meant to be a lib for a secure messeging protocol, so I was trying to leave the allocator choice up to the implementer

rocky mural
#

That's still possible, arena uses backing allocator – you can pass whatever

void orbit
#

ah ok interesting, yeah I'll read about them and test things out

rocky mural
#

You can also preheat arenas if you know your memory usage beforehand

void orbit
#

re: lifetimes and ownership .. yeah rust does force you to be aware of those things but it does hide A LOT so in some ways it makes you lazy/complacent about how allocations are being passed around and who owns what at a given time

#

especially if you end up working with tokio, the fun of async runtimes πŸ™‚

rocky mural
#

Ah

#

Zig forces you to think about them, like C. It does have a lot of niceties so if you find yourself in unholy mess of lifetimes then your code is probably badly structured.

void orbit
#

Yeah I like that about zig tbh.

rocky mural
#

In any case, after a month or so of writing Zig I feel much more comfortable with it and idioms seem natural.