Live data from Hacker News

Zig feels more practical than Rust for real-world CLI tools

dayvster.com

191–200 of 412 posts

Re: Zig feels more practical than Rust for real-world CLI tools

#191

I was going to say that it's greatly understating the value of the borrow checker. It guarantees no invalid memory accesses. But then it added: > This means that basically the borrow checker can only catch issues at comptime but it will not fix the underlying issue that is developers misunderstanding memory lifetimes or overcomplicated ownership. The compiler can only enforce the rules you’re trying to follow; it can…

> I don't see why CLI tools are special in any respect. Because they don't grow large or need a multi-person team. CLI tools tend to be one & done. In other words, it's saying "Zig, like C, doesn't scale well. Use something else for larger, longer lived codebases." This really comes across in the article's push that Zig treats you like an adult while Rust is a babysitter. This is not unlike the sentiment for Java bac…

It isn't even really that -- most CLI tools are single-threaded and have a short lifespan, so your memory allocation strategy can be as simple as allocating what you need as you go along and then letting program termination clean it up.

Re: Zig feels more practical than Rust for real-world CLI tools

#192
“Rust lifetimes can be chore, so use a C-like language that requires you to manage them in your head”

Weird that they don’t consider other options, in particular languages with reference counting or garbage collection. Those will not solve all ownership issues, but for immutable objects, they typically do. For short-running CLI tools, garbage collecting languages may even be faster than ones with manual memory management because they may be able to postpone all memory freeing until the program exits.

Re: Zig feels more practical than Rust for real-world CLI tools

#193

"All it took was some basic understanding of memory management and a bit of discipline." The words of every C programmer who created a CVE.

Segfaults go brrr. All jokes aside, it doesn’t actually take much discipline to write a small utility that stays memory safe. If you keep allocations simple, check your returns, and clean up properly, you can avoid most pitfalls. The real challenge shows up when the code grows, when inputs are hostile, or when the software has to run for years under every possible edge case. That’s where “just be careful” stops worki…

And a segfault would be worse than a panic, data corruption or out of memory access are the problems. But in reality, most C programs I use daily have never crashed in decades.

Re: Zig feels more practical than Rust for real-world CLI tools

#194

Earlier quoted context omitted.

As long as the audience accepts the framing that ergonomics doesn't matter because it can't be quantified, the hand-waving exemplified above will confound. "This chair is guaranteed not to collapse out from under you. It might be a little less comfortable and a little heavier, but most athletic people get used to that and don't even notice!" Let's quote the article: > I’d say as it currently stands Rust has poor deve…

> it's a universal tradeoff, that is: Safety is less ergonomic. I'm not sure that that tradeoff is quite so universal. GC'd languages (or even GC'd implementations like Fil-C) are equally or even more memory-safe than Rust but aren't necessarily any less ergonomic. If anything, it's not an uncommon position that GC'd languages are more ergonomic since they don't forbid some useful patterns that are difficult or impos…

The tradeoff is between performance, safety and ergonomics. With GC languages you lose the first one.

Re: Zig feels more practical than Rust for real-world CLI tools

#195

The benefit of Zig seems to be that it allows you to keep thinking like a C programmer. That may be great, but to a certain extent it’s also just a question of habit. Seasoned Rust coders don’t spend time fighting the borrow checker - their code is already written in a way that just works. Once you’ve been using Rust for a while, you don’t have to “restructure” your code to please the borrow checker, because you’ve a…

> Seasoned Rust coders don’t spend time fighting the borrow checker - their code is already written in a way that just works.

That hasn't been my experience at all. At best, the first version of code pops out quickly and cleanly because the author knows the appropriate idiom to choose. Refactoring rust code to handle changes in that allocation idiom is extremely expensive, even for the most seasoned developers.

Case in point:

> Once you’ve been using Rust for a while, you don’t have to “restructure” your code to please the borrow checker, because you’ve already thought about “oh, these two variables need to be mutated concurrently, so I’ll store them separately”.

Which fails to handle "these two variables didn't need to be mutated concurrently, but now they do".

Re: Zig feels more practical than Rust for real-world CLI tools

#196
post #160
post #114

Earlier quoted context omitted.

What about every Java/JS/Python/Rust/Go programmer who ever created a CVE? Out-of-bounds access is, indeed, a very common cause of dangerous vulnerabilities, but Zig eliminates it to the same extent as Rust. UAF is much lower on the list, to the point that non-memory-safety-related causes easily dominate it.[1] The question is, then, what price in language complexity are you willing to pay to completely avoid the 8th…

Ideally neither Zig nor Rust would matter. Languages like Modula-3 or Oberon would have taken over the world of systems programming. Unfortunately there are too many non-believers for systems programming languages with automatic resource management to take off as they should. Despite everything, kudos to Apple for pushing Swift no matter what, as it seems to be only way for adoption.

> Unfortunately there are too many non-believers for systems programming languages with automatic resource management to take off as they should.

Or those languages had other (possibly unrelated) problems that made them less attractive.

I think that in a high-economic-value, competitive activity such as software, it is tenuous to claim that something delivers a significant positive gain and at the same time that that gain is discarded for irrational reasons. I think at least one of these is likely to be false, i.e. either the gain wasn't so substantial or there were other, rational reasons to reject it.

Re: Zig feels more practical than Rust for real-world CLI tools

#197
post #132

Earlier quoted context omitted.

> thus effectively switching to automatic garbage collection Arc isn't really garbage collection. It's like a reference counted smart pointer like C++ has shared_ptr. If you drop an Arc and it's the last reference to the underlying object, it gets dropped deterministically. Garbage collection generally refers to more complex systems that periodically identify and free unused objects in a less deterministic manner.

That's fair. It's not really a good pattern though. You get all the runtime overhead of object-soup allocation patterns, syntactic noise making it harder to read than even a primitive GC language (including one using ARC by default and implementing deterministic dropping, a pattern most languages grow out of), and the ability to easily leak [0] memory because it's not a fully garbage-collected solution. As a rough ap…

I agree that using an Arc where it's unnecessary is not good form.

However, I disagree with generalizations that you can judge the quality of code based on whether or not it uses a lot of Arc. You need to understand the architecture and what's being accomplished.

Re: Zig feels more practical than Rust for real-world CLI tools

#198
post #73

Earlier quoted context omitted.

How did AWS mess up errors?

Maybe I am holding it wrong. Here is one piece of the problem: while let Some(page) = object_stream.next().await { match page { // ListObjectsV2Output Ok(p) => { if let Some(contents) = p.contents { all_objects.extend(contents); } } // SdkError Err(err) => { let raw_response = &err.raw_response(); let service_error = &err.as_service_error(); error!("ListObjectsV2Error: {:?} {:?}", &service_error, &raw_response); retu…

Out of curiosity, why are you borrowing that many times? The following should work:

    while let Some(page) = object_stream.next().await {
        match page {
            // ListObjectsV2Output
            Ok(p) => {
                if let Some(contents) = p.contents {
                    all_objects.extend(contents);
                }
            }
            // SdkError
            Err(err) => {
                let raw_response = err.raw_response();
                let service_error = err.as_service_error();
                error!("ListObjectsV2Error: {:?} {:?}", service_error, raw_response);
                return Err(S3Error::Error(format!("ListObjectsV2Error: {:?}", err)));
            }
        }
    }
I would have written it this way

    while let Some(page) = object_stream.next().await {
        let p: ListObjectsV2Output = page.map_err(|err| {
            // SdkError
            let raw_response = err.raw_response();
            let service_error = err.as_service_error();
            error!("ListObjectsV2Error: {service_error:?} {raw_response:?}");
            S3Error::Error(format!("ListObjectsV2Error: {err:?}"))
        })?;
        if let Some(contents) = p.contents {
            all_objects.extend(contents);
        }
    }
although if your crate defines `S3Error`, then I would prefer to write

    while let Some(page) = object_stream.next().await {
        if let Some(contents) = page?.contents {
            all_objects.extend(contents);
        }
    }
by implementing `From`:

    impl From> for S3Error {
        fn from(err: SdkError) -> S3Error {
            let raw_response = err.raw_response();
            let service_error = err.as_service_error();
            error!("ListObjectsV2Error: {service_error:?} {raw_response:?}");
            S3Error::Error(format!("ListObjectsV2Error: {err:?}"))
        }
    }

Re: Zig feels more practical than Rust for real-world CLI tools

#199

Earlier quoted context omitted.

"It's true when you ride a skateboard with a helmet on." Rust is not the helmet. It is not a safety net that only gives you a benefit in rare catastrophic events. Rust is your lane assist. It relieves you from the burden of constant vigilance. A C or C++ programmer that doesn't feel relief when writing Rust has never acquired the mindset that is required to produce safe, secure and reliable code.

No. It is not an invisible safeguard - it yaps and significantly increases compile time and (a matter of great debate) development effort. It is a helmet, just accept it. Helmets are useful.

The borrow checker is never a significant portion of compile times.

Re: Zig feels more practical than Rust for real-world CLI tools

#200

Earlier quoted context omitted.

> thus effectively switching to automatic garbage collection Arc isn't really garbage collection. It's like a reference counted smart pointer like C++ has shared_ptr. If you drop an Arc and it's the last reference to the underlying object, it gets dropped deterministically. Garbage collection generally refers to more complex systems that periodically identify and free unused objects in a less deterministic manner.

Reference counting has always been a way to garbage collect. Those who like garbage collection have always looked down on it because it cannot handle circular references and is typically slower than the mark and sweep garbage collectors they prefer. If you need a referecne counted garbage collector for more than a tiny minotiry of your code, then Rust was probably the wrong choice of language - use something that has…

Reference counting can be used as an input to the garbage collector.

However, the difference between Arc and a Garbage Collector is that the Arc does the cleanup at a deterministic point (when the last Arc is dropped) whereas a Garbage Collector is a separate thing that comes along and collects garbage later.

> If you need a referecne counted garbage collector for more than a tiny minotiry of your code

The purpose of Arc isn't to have a garbage collector. It's to provide shared ownership.

There is no reason to avoid Rust if you have an architecture that requires shared ownership of something. These reductionist generalizations are not accurate.

I think a lot of new Rust developers are taught that Arc shouldn't be abused, but they internalize it as "Arc is bad and must be avoided", which isn't true.

Post reply on HN