News for hackers. Stuff that matters.
Re:Zig, parser combinators, and why they're awesome by pcwalton on Wednesday March 10, 2021 @09:56PM (#26419664)
It's possible to make very small Rust binaries: https://github.com/johnthagen/min-sized-rust 8kB for that Rust example, compared to 16kB for my optimized C hello world compiled with GCC. (Not that I think it makes a difference to anything.)

Furthermore, how are Rust memory allocations and layout "fairly opaque"?

Re:Zig, parser combinators, and why they're awesome by lhorie on Wednesday March 10, 2021 @10:08PM (#26419732)
For example, usually it's not obvious when memory is being freed. I recall a thread about esbuild where the author was saying they had better GC perf from go due to GC happening in a thread whereas in Rust it didn't and it was not clear how to make that happen or if it was even possible.

In zig, you can use an arena allocator, you can free things piecemeal while using an arena, you can free the arena on program exit or in a thread, you can enforce a stack allocator, have multiple allocators, etc and because of the orthogonality of the Allocator interface, most of this knowledge is something you can probably pick up within the first 2 hours of zig (assuming you know C).

Re: edit about rust binary size. When I say zig is on par w/ C in terms of binary size, I mean I get the small binary with the basic `zig build-exe hello.zig -O ReleaseFast --strip` command, without any attempts at optimization. Do you know if this 8kb number includes the UPX optimization? If so, I'm calling shenanigans :)

Re:Zig, parser combinators, and why they're awesome by pcwalton on Wednesday March 10, 2021 @10:29PM (#26419855)
You can use arena allocators in Rust: bumpalo is popular. And it's easy to send objects off to a separate thread to be destroyed if that's what you desire: just send them over a channel to another thread (though this is really something that the malloc implementation should do internally if it's a win). There's an RFC for multiple allocators that has had some implementation work done lately: https://rust-lang.github.io/rfcs/1398-kinds-of-allocators.ht...

I don't believe that that 8kB number is with UPX, but again, even if Rust produces smaller binaries than C, it doesn't matter. Most users would prefer faster compilation times with codegen-units over a few kB smaller binaries, for instance, as long as there's a reasonable way to get the smaller binaries for those who need them.

Re:Zig, parser combinators, and why they're awesome by lhorie on Thursday March 11, 2021 @12:19AM (#26420559)
I mean, I don't want to speak for the esbuild author, but he did mention that his rust implementation ran into a number of issues[0], one of them being related to making lifetime stuff happy with concurrency. Judging from other recent rust threads, I'm not super convinced that "easy" is necessarily a fair representation of how it feels to do not-quite-bog-standard-functional-stuff w/ rust.

[0] https://news.ycombinator.com/item?id=22336284

Re:Zig, parser combinators, and why they're awesome by Dylan16807 on Wednesday March 10, 2021 @10:59PM (#26420046)
"A striped binary built this way is around 8KB."

It's before UPX.

> I mean I get the small binary with the basic `zig build-exe hello.zig -O ReleaseFast --strip` command, without any attempts at optimization.

You could set up all these things in just a few minutes, so it's not a very big deal. If you don't mind 20KB of string formatting code then you don't have to make "any attempts at optimization" either.

Re:Zig, parser combinators, and why they're awesome by lhorie on Wednesday March 10, 2021 @11:49PM (#26420389)
> If you don't mind 20KB of string formatting code

So this here is sort of why I brought up binary size. I know hello world size doesn't really say anything about real world, but as I was looking at different languages, a common theme was this defensive "well what d'ya expect from a thing that has [list of features]" tone when the question of binary size got brought up and people didn't know how to make it smaller. A normal rust build has a similar feel. The 8kb binary link above reads a bit like a hacking adventure to unravel the mysteries of yonder.

When I read stuff like this

    By using a C entry point (by added the #![no_main] attribute) , managing stdio manually, and carefully analyzing which chunks of code you or your dependencies include, you can sometimes make use of libstd while avoiding bloated core::fmt.

    Expect the code to be hacky and unportable, with more unsafe{}s than usual. It feels like no_std, but with libstd.
I'm undoubtedly awed at the cleverness of it all, but it's clearly way off the beaten path.

I should note that in addition to the zig build command being guessable by a newbie reading through --help, the hello world code itself is also the idiomatic thing a newbie would've reasonably written. And there's really no need for "air quotes", I really put no effort whatsoever in optimizing: I didn't even use `-O ReleaseSmall`

Among all the languages I looked at, Zig was somewhat unique in taking a default stance of proactively using compile time as a tool to take stuff away from the binary (in a sort of saint-exupery way).

I'm sure with enough effort it's possible to bring down binary sizes for most languages, but not many languages do this with the relentlessness that zig does out of the box.

Re:Zig, parser combinators, and why they're awesome by Dylan16807 on Thursday March 11, 2021 @12:33AM (#26420633)
> So this here is sort of why I brought up binary size. I know hello world size doesn't really say anything about real world, but as I was looking at different languages, a common theme was this defensive "well what d'ya expect from a thing that has [list of features]" tone when the question of binary size got brought up and people didn't know how to make it smaller.

Well, what do you expect? How much size does printf add to a Zig binary?

> When I read stuff like this

You can get the vast majority of the size savings without any of that stuff. This is someone trying to find the absolute minimum, starting with very practical suggestions before getting into itty bitty weeds. I'd suggest pretending the no_main and no_std sections don't exist.

Getting down to 50KB or 30KB is just a matter of telling the compiler to optimize for size and stripping unwind information.

If you want to keep all the unwind code and information I'm not sure how much space it takes.

> with enough effort

I will note again that the compiler settings in this repo, while they may be in multiple places, are still a very small one-time effort for a program.

Re:Zig, parser combinators, and why they're awesome by lhorie on Thursday March 11, 2021 @01:28AM (#26420926)
Well, it's not so much about expectations about byte counts per se. YMMV but in my experience, for whatever reason the relative size of an average program happens to correlate with how big and complex a project gets as a function of its growth. For example, the Factor distribution is some 700MB and unsurprisingly a hello world image is also huge. A while back I also saw one analysis about jquery/vue/angular/react sites and curiously framework size was also a good predictor of average app size in the wild.

So I use the size metric as a coarse proxy for "complexity required to accomplish the task at hand". And if one is to believe the mantra "to be fast, do less stuff", then my instinct is to look for technologies whose proxy metric (i.e. idiomatic hello world size) is small.

Maybe there are better ways to measure this, but so far this heuristic has worked for me.

> How much size does printf add to a Zig binary?

It depends, because comptime. Basically, it emits different code depending on the types of the params being passed in. For hello world, it wouldn't include itoa logic, for example. The irony here of course is that zig could in theory compile to a very very large binary if it had a very very large number of printf calls, all with different permutations of types. But the thing is that to a zig developer, it's clear that this is explicit static polymorphism getting compiled into a plethora of monomorphic callsites. One could implement the equivalent of go's `interface {}` underlying {type, value} tuple and have a single polymorphic function at runtime if that's what they wanted. But this same argument about how it would require someone manually implementing such a thing goes both ways: it also means that most other possible things are likely not going to be hanging around quietly adding complexity under the rug (not saying this is the case w/ rust, but it is with various other systems).

Re:Zig, parser combinators, and why they're awesome by volta83 on Thursday March 11, 2021 @06:01AM (#26422687)
> But the thing is that to a zig developer, it's clear that this is explicit static polymorphism getting compiled into a plethora of monomorphic callsites

To a Rust programmer it is clear that the toolchain defaults are set up to satisfy the 99.99999999% of programmers whose goal isn't to "write the smallest hello world possible".

You can change these defaults in your system config if you don't like them.

Like you mention, one size doesn't fit all.

To me that's one of the main differences between Zig and Rust.

Re:Zig, parser combinators, and why they're awesome by lhorie on Thursday March 11, 2021 @07:50AM (#26423417)
It's also not entirely about clarity of facts per se, but rather which facts. It's clear for a javascript developer that doing NPM install is going to bring in half the universe worth of transitive deps and that they can opt out by writing things by hand.

Most js developers' goals are indeed not to write the smallest hello world, and here we are in a world where people complain of news websites pulling MBs of scripts. As I mentioned, one could conceivably address this by merely picking jquery over react in many cases, for example. There's a predictable sliding scale of performance between using electron, nodegui and qt proper via c++. Etc. All of this is to say: Defaults and base choices matter.

I think in terms of servicing end users, rust and c++ are somewhat unique in that they are relatively complex languages targeting ambitious performance goals, at the cost of more language complexity. That has its own appeal for sure, but I echo esbuild author's sentiment about a language having to be fun for side projects, and for me personally, I like the simple "very sharp knife" variety of technologies

Re:Zig, parser combinators, and why they're awesome by user-the-name on Thursday March 11, 2021 @08:27AM (#26423744)
It's not clear to me.

I'm not a Rust programmer, but I've written several projects where I'm pulling in a large Rust dependency, because the best available library for something is implemented in Rust, and it offers a C interface.

These libraries I've used have been very good, but also, extremely large. Like, over 100 MB .a files. They slow down compilation noticeably just by linking, and while a lot of the size is stripped out during linking, the resulting sizes tend to still be uncomfortably big.

So to me it seems Rust binaries tend to be huge both on the small end large ends of the spectrum, and they are large to the point where it becomes a real problem.

Re:Zig, parser combinators, and why they're awesome by volta83 on Thursday March 11, 2021 @04:41PM (#26429345)
You should open an issue with whoever packages that library.

Nobody expects library consumers to be experts in how to build libraries.

But this isn't any different that depending on a C library, and discovering that the Makefile they are using compiles it with -O0, and makes it super slow.

To a non-C programmer, flipping -O0 to -O3 is wizardry.

---

Point being, you are not a rust programmer, and the defaults that are good for "development" are not good for "shipping" and therefore no good for you. If your library doesn't do this right, open a bug. The library can override this, and a package that builds it should override this.

Re:Zig, parser combinators, and why they're awesome by user-the-name on Friday March 12, 2021 @05:31AM (#26434177)
No, this is a fully optimised release build, built according to official instructions.

Rust code is just very big. This seems to come with the territory.

Re:Zig, parser combinators, and why they're awesome by volta83 on Tuesday March 16, 2021 @05:38AM (#26474649)
> Rust code is just very big. This seems to come with the territory.

As someone that uses Rust every day to program STM32s with 128KB of space... i... disagree...

Rust is the only high level language I know that allows me to customize the language run-time at will, allowing me to provide my own unwinding, threading, memory allocation, ... runtimes, to be able to program tiny machines as if I was just programming any normal one.

With other languages either I can use the standard library implementation, or I can't use the standard library and some language features at all.

With Rust, I can't use the standard library implementation for desktops, but I can just swap its backend by an embedded one, and am still able to re-use all the high level code, libraries from crates.io that use it, etc.

Re:Zig, parser combinators, and why they're awesome by AndyKelley on Wednesday March 10, 2021 @11:24PM (#26420218)
for zig it's 1.8 kB on x86_64-linux with minimal fiddling. i386-windows comes out to 2.5kB.

    $ cat hello.zig
    const std = @import("std");
    pub fn main() void {
          std.io.getStdOut().writeAll("Hello, World!\n") catch {};
    }
    $ zig build-exe hello.zig --strip -OReleaseSmall
    $ zig build-exe hello.zig --strip -OReleaseSmall -target i386-windows
    $ ldd hello
     not a dynamic executable
    $ strip hello
    $ ./hello
    Hello, World!
    $ wine hello.exe
    Hello, World!
    $ ls -hl hello hello.exe
    -rwxr-xr-x 1 andy users 1.8K Mar 10 21:15 hello
    -rwxr-xr-x 1 andy users 2.5K Mar 10 21:15 hello.exe

No min-sized-zig github repository needed.
Re:Zig, parser combinators, and why they're awesome by steveklabnik on Wednesday March 10, 2021 @11:57PM (#26420425)
I work in embedded these days, commented on this topic on Reddit a few days ago: https://www.reddit.com/r/rust/comments/m0irjk/opinions_on_ru...
Re:Zig, parser combinators, and why they're awesome by anderspitman on Wednesday March 10, 2021 @10:02PM (#26419698)
Not parent, but from my perspective implicit RAII might fall into this category, though I suppose that's specific to deallocation. Zig also requires you to pass an allocator to any function that requires dynamic allocation, which while this could be considered a bit extreme, is certainly less "opaque".