Comment by 8s2ngy

Comment by 8s2ngy a day ago

55 replies

I’m sorry, but any non-trivial Zig code gives me PTSD flashbacks of C. I don’t understand who Zig is targeting: with pervasive mutability, manual allocation, and a lack of proper sum types, it feels like a step back from languages such as Rust. If it is indeed a different way to write code, one that embraces default memory unsafety, why would I choose it over C, which has decades of work behind it?

Am I missing some context? I’d love to hear it.

sothatsit a day ago

I love Zig precisely because it is so similar to C. Honestly, if you don't like C, I can totally understand why you wouldn't like Zig. But I love C, and I love Zig.

Zig has become my go-to for projects where I would previously have reached for C, largely because Zig has such good compatibility with other C projects.

Rust, on the other hand, is a completely different beast. It is very different from C, and it is far more complicated. That makes it harder to justify using, whereas Zig is a very easy choice as an alternative to using C itself.

  • simonask a day ago

    C is entirely as complicated as Rust, if your goal is to write correct software that doesn't crash all the time. It's only a syntactically simple language. Actually making anything interesting with it is _not_ simple.

    • kstenerud a day ago

      Quite right. I have 35 years of C under my belt. I can write it in my sleep.

      But even so, I can't for the life of me write C code that's as safe as Rust. There are just too many ways to make subtle little mistakes here and there, incrementing a typed pointer by a sizeof by mistake thinking it's a uintptr_t, losing track of ownership and getting a use-after-free, messing up atomic access, mutex deadlocks oh my...

      And that's with ALL warnings enabled in CLANG. It's even worse with the default warnings.

    • sothatsit a day ago

      It depends on the project. Most of the projects I write in C are very simple, and getting them to work reliably is really not a problem at all.

      If you are writing more complicated or "interesting" programs, then I agree, C doesn't give you a good set of tools. But if all you are writing is small libraries or utility programs, C is just fine. In these cases, Rust feels like pulling out a sniper rifle to shoot a target a meter in front of your face (i.e., overkill).

      If you are writing complex, large, or very mission-critical programs, then Rust is great to have as a tool as well. But we don't have to take such a black and white view to think that Rust is always the best tool for the job. Or C or Zig or whatever languages for that matter.

    • uecker a day ago

      Most software I use daily on my Linux system is actually written in C and I can't remember any of it crashing in the last decade or so.

      • simonask a day ago

        Yeah, a ton of engineering hours went into making that happen.

    • taminka a day ago
      • simonask a day ago

        I don't think a 100-line function signature is representative, but I will point out that the alternative is at least 100 lines of runtime checks instead. In both cases, what a nightmare.

      • jeroenhd a day ago

        Typing code from hell for sure, but how would you write an API with the same guarantees in C? Some kind of method specific struct that composes all other kinds of structs/unions to satisfy these requirements?

      • kelnos a day ago

        To me that's more an indictment of Diesel than of Rust. I've been using sea-orm for a project I'm working on, and my (generic) pagination function is a hell of a lot simpler and readable than that one.

      • viraptor a day ago

        This is an extremely generic interface to some meta magic DSL. It's complex but not really that complicated and yeah, it's going to be a bit long. But that's going to happen in every language where you rely on types for early validation.

      • lll-o-lll a day ago

        Yuck. I thought some of the signatures you end up with when building “Modern C++” in the Andrei Alexandrescu style were hairy, but this looks sick. Not in a good way.

        Probably does something cool for all that crazy though?

        • jeroenhd a day ago

          Every requirement on the types is commented on why it's necessary.

          This is a generic method in the middle of some database DSL code that does a bunch of SQL operations on a type safe manner. Code like this takes "SELECT ?+* FROM ?+* WHERE ? ORDER BY ?+* LIMIT ? OFFSET ?", specifically the limit and offset part, and returns a type that will always map to the database column. If the query is selecting a count of how many Foo each Baz references, this will map to a paginated Foo to Baz count type.

          The alternative is to manually write this stuff out in SQL, then manually cast the right types into the basic primitives, which is what a language like Zig probably does.

          You'll find similar (though sometimes less type-safe) complex code in just about any ORM/DSL, whether it's written in Java or PHP.

          I don't think you can accomplish this in C without some kind of recursive macro parser generating either structs or maybe function pointers on the fly. It'd be hell to make that stuff not leak or double free memory, though.

      • norskeld a day ago

        As a TypeScript developer experienced in type-level acrobatics, this looks just fine...

ozgrakkurt a day ago

Compared to C:

Discriminated unions, error handling, comptime, defer.

Better default integer type casting, ability to choose between releaseSafe/releaseFast

And probably other things.

As for comparison to Rust, you do want very low level memory handling for writing databases as an example. It is extremely difficult to write low level libraries in Rust

  • simonask a day ago

    I think the argument is that it is also extremely difficult to write low level libraries in Zig, just as it is in C. You will just only notice the difficulty at some later point after writing the code, potentially in production.

    • flohofwoe a day ago

      > low level libraries in Zig, just as it is in C

      Did you write any Zig code yet? In terms of enforced correctness in the language (e.g. no integer promotion, no implicit 'dangerous' casts, null-safety, enforced error handling, etc...) and runtime safety (range-, nullptr-, integer-overflow-checks etc...), Zig is much closer to Rust than it is to C and C++.

      It "just" doesn't solve static memory safety and some (admittedly important) temporal memory safety issues (aka "use-after-free"), but it still makes it much harder to accidentially trigger memory corruption as a side effect in most situations that C and C++ let slip through via a mix of compile errors and runtime checks (and you get ASAN/UBSAN automatically enabled in debug builds, a debug allocator which detects memory leaks and use-after-free for heap-allocations (unfortunately not for stack allocations), and proper runtime stack traces - things that many C/C++ toolchains are still missing or don't enable by default).

      There is still one notable issue: returning a reference to stack memory from a function - this is something that many unexperienced Zig programmers seem to stumble into, especially since Zig's slice syntax looks so 'innocent' (slices look too similar to arrays, but arrays are values, while slices are references - e.g. 'fat pointers') - and which IMHO needs some sort of solution (either a compile time error via watertight escape analysis, or at least some sort runtime check which panics when trying to access 'stale' data on the stack) - and maybe giving slices their own distinct syntax that doesn't overlap with arrays might also help a bit.

      • simonask a day ago

        I mean, there's no question that Zig, also in its current state, is vast improvement over C or even C++ - for the "small stuff". It is much more pleasant to use.

        But there is still the "big stuff" - the things that have a fundamental, architectural impact. Things like: Will my program be multithreaded? Will I have many systems that interact? Will my program be maximally memory-efficient? Do I have the capacity (or money) to ensure correctness if I say "yes" to any of that?

        The most important consideration in any software project is managing architectural complexity. Zig is better, yes, but not a paradigm shift. If you say "yes" to any of the above, you are in the same world of pain (or expenses) as you would be in C or C++. This is the reason that Rust is interesting: It makes things feasible/cheap that were previously very hard/expensive, at a fundamental level.

    • kristoff_it a day ago

      > I think the argument is that it is also extremely difficult to write low level libraries in Zig, just as it is in C.

      This has been not my experience at all in the ~6 years I've been writing Zig. I started having very little experience writing C (<1000, lines all written while in university) and since day 1 Zig has been a tremendous improvement over it, even back when it was at version 0.4.0.

      • simonask a day ago

        Glad you're having a great time with it. :-)

        I'm informed by having shipped a lot of C++ code in my time, which has taught me a lot about actually delivering stable software. Being better than C is a very low bar here.

    • ozgrakkurt a day ago

      This is a subjective argument. You don’t know me, I don’t know you. There is no meaning in assuming anything.

      There is plenty of working software written with pretty much any language

  • kstenerud a day ago

    > It is extremely difficult to write low level libraries in Rust

    Really? I've not found it at all difficult to write low level libraries in Rust.

    • ozgrakkurt a day ago

      For me it was very difficult to make an io library on io_uring that is properly safe.

      Also using arena and other special allocators in different sections of the program. While maintaining hard memory limits for different sections of the program.

      These are possible to do in rust but it is very difficult for me so I decided to just not do it. Otherwise I have to spend 5x the normal amount of time to make sure the library cannot be misused by the user ever.

      This is pretty pointless since I’m the only one who is going to use that code anyway.

      Could be skill issue or w/e but I just find zig easier to make progress in

flohofwoe a day ago

> a lack of proper sum types

Do you consider Rust enums 'proper sum types'? If yes what are Zig's tagged unions missing?

E.g.:

    const Variant = union(enum) {
        int: i32,
        boolean: bool,
        none,

        fn truthy(self: Variant) bool {
            return switch (self) {
                Variant.int => |x_int| x_int != 0,
                Variant.boolean => |x_bool| x_bool,
                Variant.none => false,
            };
        }
    };
kllrnohj a day ago

Zig is for people who want to write C, that's really it. It's not a replacement for C++ or Rust or Go or Swift or anything "serious".

As for why you would choose it over C, because C has too many problems for even the C lovers to ignore. Zig fixes a tiny amount of them, just enough to pretend it's not problematic, but not enough to be useful in any non-hobby capacity. Which is fine, very few languages do achieve non-hobby status after all.

LAC-Tech a day ago

Zig is a systems programming language. I think that's probably who it's targeting.

People do systems programming in rust, but that's not really what most of the community is doing. And it's DEFINITELY not what the standard library is designed for.

  • konart a day ago

    >People do systems programming in rust, but that's not really what most of the community is doing.

    As someone who haven't done any systems programming after university: wait, what?

    I was under impression that this is exactly what people where doing with Rust.(system apps, even linux kernel, no?)

    If not - what do they (most if the community) are doing with Rust?

    • LAC-Tech a day ago

      Web servers, games, and applications, that sort of thing.

      Some people definitely do systems programming in, but it's a minority. The std library is not set up for it at all, you need something like rustix, but even that results in very unidiomatic ("unsafe") rust code.

      In Zig it's all in the std library by default. Because it's a systems programming language, first and foremost.

      • Ar-Curunir a day ago

        Rust is in the Linux kernel. Doesn’t get more systems than that…

      • porridgeraisin a day ago

        Actually I was also under OPs impression... can you tell me few specific problems with using rust for systems programming? BTW, I have only ever done something that resembles systems programming in C.

  • simonask a day ago

    Which part of the Rust standard library are you referring to here?

    As far as I can tell, it contains many, many features that are irrelevant outside of systems programming scenarios with highly particular needs.

    • LAC-Tech a day ago

      Let me answer your question with a question - how do you memory map in rust with the standard library?

      In zig it's std.posix.mmap.

      • tialaramex a day ago

        Because Rust's standard library doesn't provide memory mapping you will need to use platform specific APIs.

        In Zig it's exactly the same except that they decided to provide the POSIX platform specific APIs, which if you're using a POSIX system is I guess useful and otherwise it's dead weight.

        It's a choice. I don't think it's a good choice but it's a choice.

      • zelphirkalt a day ago

        Your question might hint at a questionable presumption. So let me answer your question with a question - Does one have to memory map in Rust? Perhaps there are alternatives available in Rust, that you are not considering.

      • simonask a day ago

        I think you are moving the goal posts. You use the `memmap2` crate, or the `libc` crate if you want to be reckless about it. The question was how the standard library gets in your way, not whether it includes everything you need.

        And I don't think that including every feature of every possible OS is a sensible position to have for a standard library. Or would you argue that it should also include things like `CreateWindowExW()`?

        If all you use is the Rust standard library, you can be reasonably sure that your program works on all platforms, and memory mapping is something that is highly platform specific, even among POSIX-likes. I would not like to attempt designing a singular cross-platform API for it that has to be maintained in perpetuity.

        (There are a few OS-specific APIs in the Rust standard library, mostly because they are required anyway for things like I/O and process management. But the limit has to be set somewhere.)

      • kryptiskt a day ago

            extern "C" mmap(addr:*mut c_void, length:c_size_t, prot:c_int, flags:c_int, fd:c_int, offset:c_ssize_t) -> *mut c_void;
        
        Piece of cake. Or you could install a crate with bindings if you are afraid of writing code yourself.
      • [removed] a day ago
        [deleted]