Kang Seonghoon, a Rust and JavaScript programmer, writes about executable size, compression, and packing code for size-limited demos.
HN links his post on why Rust executables are large; commenters also cite his work on demo compressors and shrinking Rust binaries.
> If it was, there wouldn't be new package managers popping up all the time; it's a non-trivial problem. They're not created for no reason.
Notice how all those package managers are for platforms or create platforms in their own right. Rust is meant to be a systems language, that means it's platform is the OS and it doesn't get to be a world unto itself like java.
> This isn't exactly true, or rather, you're comparing two different things. https://lifthrasiir.github.io/rustlog/why-is-a-rust-executab... has the details.
So if you jump through a million hoops, limit yourself to c libraries you can produce small executables. At that point it's more complicated than just writing an app in c in the first place.
I'm not interested in what it can technically do though, I'm interested in what is practically happening. In practice most rust programmers seem to be writing apps for the cargo platform. In practice rust developers are producing huge executables. In practice rust has no stable ABI so all rust libraries get statically compiled. In practice this is incompatible with the LGPL. In practice a security vulnerability means every app using the library has to be recompiled to be secure.
> Rust makes enormous binaries
Rust binaries are optimized to be fast (by default, fast != small), this [0] explains pretty well why, and min-sized also has many optimizations [1].
> and compiles slow
You can have a very slow compiler with all the optimizations and safety checks, or a fast one which doesn't. This however doesn't mean there is no room for improvement.
One thing that can definitely improved on is how cargo manages dependencies, I am tired of each of my project's /target directory using >2GB of disk space, there at least should be some kind of opt-in global cache.
I am also worried about dependencies going in the same direction as the JS ecosystem, many of my projects only have 3-5 top level dependencies, but I end up downloading hundreds. A few are only compile-time, but this makes auditing very difficult and does make compile times a lot longer.
[0]: https://lifthrasiir.github.io/rustlog/why-is-a-rust-executable-large.html
[1]: https://github.com/johnthagen/min-sized-rust
Actually, the culprit is Rust's decision to statically link its standard library and all its dependencies by default.
Things like libunwind, libbacktrace, embedded debugging symbols for backtraces, and the jemalloc allocator aren't free.
If you ask for dynamic linkage (with the caveat that Rust doesn't have a stable ABI yet), you get a ~8K Hello World binary.
It's also possible to prune down the statically-linked size by opting out of various conveniences like jemalloc. (They're working toward making the system allocator default but don't want to regress Servo in the interim.)
...and if opt into static linking with GCC and G++ (and ask Rust to make its link to libc static), Rust can actually outdo them on a Hello World.
Here's a detailed exploration: https://lifthrasiir.github.io/rustlog/why-is-a-rust-executable-large.html
It's a matter of platform and the libraries that you're using.
After all, back in the days we had 360k floppy disks, and executable written in C which did much more than just printing out "Hello world" would fit comfortably into less than half of that.
Modern C and C++ runtimes are bloated, because a 2MB executable isn't considered huge anymore and dynamic linking is common.
But you can have a 5k static executable printing "hello world" on Linux if you just trade in your stdlibc to musl.
People have also managed to use musl with Rust to produce pretty small executables:
https://lifthrasiir.github.io/rustlog/why-is-a-rust-executable-large.html
There's a lot that can be done to shrink a Rust binary[0]. A copy of the summary:
- Compile with --release.
- Before distribution, enable LTO and strip the binary.
- If your program is not memory-intensive, use the system allocator (assuming nightly).
- You may be able to use the optimization level s/z in the future as well.
- I didn’t mention this because it doesn’t improve such a small program, but you can also try UPX and other executable compressors if you are working with a much larger application.
[0]https://lifthrasiir.github.io/rustlog/why-is-a-rust-executable-large.html
The bloat issues are (a) a constant overhead and (b) a matter of defaults.
See https://lifthrasiir.github.io/rustlog/why-is-a-rust-executable-large.html#takeaway
Rust statically links to libstd by default because it's not there as a dynamic lib on most platforms. On top of that, it links in jemalloc. This makes small Rust binaries look larger than they need to be. The overhead dwindles as you start looking at larger Rust programs. If you actually need to get rid of that overhead, the option is there, it's just not default.
Addition to popular package managers is being worked on.