Live data from Hacker News

How (memory) safe is Zig? (2021)

scattered-thoughts.net

81–88 of 88 posts

Re: How (memory) safe is Zig? (2021)

#81

Earlier quoted context omitted.

because it gets it out of the fast path compile cycle. do you need a borrow checker for `ls`? Probably not. don't use it. do you need it every time you work through intermediate ideas in a refactor? probably not. just turn it on in CI.

The borrow checker is not the slow part of the Rust compiler and lets me avoid bugs, why would I not always want to use it? And if you put the borrow checker in the CI you massively increased the latency between writing the code and getting all relevant feedback from the compiler/tooling. This would do the opposite of what you intended.

You don't have to query all checks at all times. You only have to borrow check when you pr to main. What is the sum latency of the coder dealing with conceptual problem of satisfying the borrow checker at every stage during an arduous refactor where they don't fully have the destination model in mind (and might want to try things out only to discard them). Versus, just ignoring it, seeing how the chips fall, and fixing ownership errors in one or two passes at the end once you've settled om the code structure? Of course you don't have to take the full round trip-to-someone-else's-computer latency either. If you can run it in CI, I presume you could run it locally too, and borrow checking is not particularly slow for the computer.

You're also not (because one can't) quantifying the problem of a developer getting exasperated and saying, fuck it, it passes the borrow checker, good enough, instead of actually taking the time to make their code legible first and then making sure it is memory safe. This absolutely happens.

Re: How (memory) safe is Zig? (2021)

#82
post #74

Earlier quoted context omitted.

because it gets it out of the fast path compile cycle. do you need a borrow checker for `ls`? Probably not. don't use it. do you need it every time you work through intermediate ideas in a refactor? probably not. just turn it on in CI.

> do you need a borrow checker for `ls`? Probably not. Does ls use references and objects with lifetimes? I bet it does. And if so, the answer is yes. You do need the borrow checker in rust to make sure it uses memory and lifetimes correctly. If your program somehow doesn’t use references or owned objects, then the borrow checker doesn’t have any work to do. So there’s no harm done in leaving it on.

[deleted]

Re: How (memory) safe is Zig? (2021)

#83
post #73

Earlier quoted context omitted.

> lots of people would "accidentally forget" to run it yeah, like how the sel4 guys accidentally forget to run their static analysis all the time. You put a badge on CI. If you "forget to run" the static analysis, then people get on you for not running it. Or people get on you if you don't have the badge. Just like how people get on people for not writing programs in rust.

Q: If we can get men on the moon, why is my lawnmower such a piece of junk? A: The engineers who got us in to space didn’t design your lawnmower. The SeL4 team won’t forget to run their static analysis checks. But most people aren’t at their level. Most people just want to get on with it. The borrow checker is a pain in the neck to learn - and if it were optional, you better believe lots of people would avoid it fore…

your argument doesn't hold water, because you are appealing to the existence of a schroedinger's programmer that simultaneously would use rust but also wouldn't turn on a borrow checker if it existed in another language.

My point is the fact that people use Rust means that people do care. And those people will run static analysis checks (as long those static analysis checks aren't the shitty or overly difficult to run), and they will notice that your library isn't checked. Or, better yet, they will integrate it into their project, run the checker on their project, find the problem in the lazy person's dependency and PR a fix or fork it if the maintainer is an asshole.

Here's the thing. Rust is currently "the only game in town" for spatiotemporal memory safety and that is sucking the air out of the atmosphere, because no one wants to invest time into trying other ideas. And that's a shame, because everyone makes assumptions that Rust must be the only way to do spatiotemporal safety (cough cough Ada/Spark). And it gives cover for people to claim things that are presumptuous and totally untested like our schroedinger's programmer here.

Rust has a lot lot lot of shitty things in it, like RAII, and proc macros, and pushing you into some incredibly complicated (and privileged) templated types that are doing some things under the hood (remember when rust switched from jemalloc to malloc?), and Rust hasn't ever gotten to keyword generics yet. Those of us who don't want that kind of BS in our language have to constantly be accused of not caring about memory safety which is hardly the truth.

Re: How (memory) safe is Zig? (2021)

#84

Earlier quoted context omitted.

The borrow checker is very fast.

computationally. But it slows down the programmer. It is not a zero-cost human operation. If it were, we wouldn't need computers to do it.

I don’t think it slows me down, it speeds me up. It’s well known that surfacing issues earlier in a process saves time compared to later in a process.

Re: How (memory) safe is Zig? (2021)

#85
post #73

Earlier quoted context omitted.

Q: If we can get men on the moon, why is my lawnmower such a piece of junk? A: The engineers who got us in to space didn’t design your lawnmower. The SeL4 team won’t forget to run their static analysis checks. But most people aren’t at their level. Most people just want to get on with it. The borrow checker is a pain in the neck to learn - and if it were optional, you better believe lots of people would avoid it fore…

your argument doesn't hold water, because you are appealing to the existence of a schroedinger's programmer that simultaneously would use rust but also wouldn't turn on a borrow checker if it existed in another language. My point is the fact that people use Rust means that people do care. And those people will run static analysis checks (as long those static analysis checks aren't the shitty or overly difficult to ru…

> your argument doesn't hold water, because you are appealing to the existence of a schroedinger's programmer that simultaneously would use rust but also wouldn't turn on a borrow checker if it existed in another language.

I'm appealing to the existence of people who would like rust very much, but find the borrow checker to be cumbersome / annoying / difficult. I suspect a lot of people would fall into this camp. Including a lot of people who admire Zig, Jai, Odin, dmd. And probably a lot of C++ developers.

In my opinion, most of the best parts of rust have nothing to do with the borrow checker. For example, sum types, the rust standard library, cargo & crates.io, traits, Result / Option, editions, rust's unit testing, etc.

If someone released a borrow checker for zig, would it get 100% adoption? No way. Especially if it was hard to learn, required adding unfamiliar "lifetime annotations" throughout your code and sometimes required you to refactor functions that you know are correct anyway.

> Here's the thing. Rust is currently "the only game in town" for spatiotemporal memory safety and that is sucking the air out of the atmosphere, because no one wants to invest time into trying other ideas. [...] Those of us who don't want that kind of BS in our language have to constantly be accused of not caring about memory safety which is hardly the truth.

I think we've found agreement. I've been saying this for years - Rust is humanity's first serious attempt at a programming language like this. Its like the first iPad. I think there's lots of ways a rust successor could learn from rust's mistakes and make a better language. I'm really looking forward to what a borrow checker might look like a few more languages down the line.

Re: How (memory) safe is Zig? (2021)

#86
post #57

Earlier quoted context omitted.

I don't think Zig gives you significantly more control than Rust.

Maybe not Zig the language, but the fact that all allocating functions in the standard library accept an allocator (and community libraries follow this precedent) does give you much more control in practice. For example, how would you use a Vec using stack memory for elements, instead of the heap? For the equivalent data structure in Zig (std.ArrayList), it's just a matter of using a stack allocator instead of using…

> For example, how would you use a Vec using stack memory for elements, instead of the heap? For the equivalent data structure in Zig (std.ArrayList), it's just a matter of using a stack allocator instead of using a heap allocator, which is an explicit decision either way.

In Rust it would likewise "just" be a matter of using the allocator we want. In this specific case we can see that's nonsense - there's a single stack pointer in the CPU so while it's perfectly possible to make two growable arrays (Vec or ArrayList depending on the language) on the heap, if we use the stack instead they're both obliged to somehow share that single stack pointer when growing, thus whether Zig or Rust this idea can't actually work, but for examples which do work the Rust and Zig doesn't look that different.

Re: How (memory) safe is Zig? (2021)

#87
post #57

Earlier quoted context omitted.

Maybe not Zig the language, but the fact that all allocating functions in the standard library accept an allocator (and community libraries follow this precedent) does give you much more control in practice. For example, how would you use a Vec using stack memory for elements, instead of the heap? For the equivalent data structure in Zig (std.ArrayList), it's just a matter of using a stack allocator instead of using…

> For example, how would you use a Vec using stack memory for elements, instead of the heap? For the equivalent data structure in Zig (std.ArrayList), it's just a matter of using a stack allocator instead of using a heap allocator, which is an explicit decision either way. In Rust it would likewise "just" be a matter of using the allocator we want. In this specific case we can see that's nonsense - there's a single s…

Welcome to the magic of Zig, where ideas that can't actually work actually do work :-)

    const std = @import("std");

    pub fn main() !void {
        var not_heap_memory: [1024]u8 = undefined;
        var fixed_buffer_allocator = std.heap.FixedBufferAllocator.init(&not_heap_memory);
        const allocator = fixed_buffer_allocator.allocator();
        var my_list = std.ArrayList(i32).init(allocator);
        defer my_list.deinit();

        // notice the try! allocation may fail!
        try my_list.append(1);
        try my_list.append(2);
    
        for (my_list.items) |d| {
            std.debug.print("{}\n", .{d});
        }
    }
The value here is not that you have unbounded stack memory - obviously. The value is that you can use the same API for a growable list (std.ArrayList) backed by stack memory, as you would for a growable list backed by heap memory. This could be useful if you have a function that accepts an ArrayList as an argument and will append to it, but you have a known maximum size for the ArrayList in the context that you're using it. You cannot use the standard Vec type in the same way in Rust. You cannot use any data structures from the Rust standard library that do allocations in the same way.

Yes, of course, you can make data structures that accept custom allocators in the same way in Rust. Maybe there are even community libraries that do it already. The problem is that because they're not in the standard library, you're going to have a hard time using those data structures with any other community libraries. And thus, in practice, you have less control over your allocation strategies in Rust than you do in Zig.

Re: How (memory) safe is Zig? (2021)

#88
post #87

Earlier quoted context omitted.

> For example, how would you use a Vec using stack memory for elements, instead of the heap? For the equivalent data structure in Zig (std.ArrayList), it's just a matter of using a stack allocator instead of using a heap allocator, which is an explicit decision either way. In Rust it would likewise "just" be a matter of using the allocator we want. In this specific case we can see that's nonsense - there's a single s…

Welcome to the magic of Zig, where ideas that can't actually work actually do work :-) const std = @import("std"); pub fn main() !void { var not_heap_memory: [1024]u8 = undefined; var fixed_buffer_allocator = std.heap.FixedBufferAllocator.init(&not_heap_memory); const allocator = fixed_buffer_allocator.allocator(); var my_list = std.ArrayList(i32).init(allocator); defer my_list.deinit(); // notice the try! allocation…

> The problem is that because they're not in the standard library, you're going to have a hard time using those data structures with any other community libraries.

No, all the Rust standard library collections do in fact have the same feature, you can in fact Vec::new_in(SomeAllocator) - my guess is that you'll say "Ah, but that's not yet in stable Rust" and that'd make sense in other contexts but it's a weird objection when the entire Zig language still isn't 1.0.

If you wanted to write a function which takes a container and adds things to it, which I wouldn't recommend, in Rust you'd write that as a polymorphic function, so it'll just get monomorphized for the SomeAllocator variant.

The use of FixedBufferAllocator here is absurd, indeed it's hard to think of non-absurd uses for this allocator, it's a toy because it has no reclamation.

ArrayList has a slightly weird variation on the 1.5x growth pattern, for a large T the ArrayList will grow something like 1, 2, 4, 7, 11, 17, 26, etc. But with your 1024 byte FixedBufferAllocator, the older sizes are just discarded so somewhere around 62 or so items it'll blow up, all the rest of the space was just thrown away and so the growth fails

Overall this not only doesn't do what you said you were doing originally (it's not actually a stack† allocated growable array, those simply don't exist), it also doesn't do the thing you ostensibly claim it's useful for either, so much of Zig feels like this.

† Edited: For a few minute this said "heap" due to a thinko

Post reply on HN