Show HN: Lstr – A modern, interactive tree command written in Rust
36 comments
·June 18, 2025ipdashc
Seems quite cool! Though the demo gif with (what seem to be?) broken icons is a bold choice :p
w108bmg
lol good catch, and I totally missed it
I used vhs to record the gif which must not run the script in my native terminal! I’ll have to see about fixing it!
yonatan8070
I've seen a lot of people use asciinema to record and share terminal recordings, it works quite well
sdegutis
I didn't notice, I was too busy seeing how impressive and useful this tool is.
And with fuzzy matching built in? Just amazing. Good job OP.
w108bmg
Really appreciate all the comments and useful feedback (first Rust package). Especially ways to reduce the size of the binary!
drabbiticus
First off, the display looks great!
Second off, I didn't realize how deep the dep tree would be for this type of program -- 141 total! So much of it is the url crate, itself a dep of the git crate, but there's a bunch of others too. I'm just getting into learning Rust -- is this typical of Rust projects or perhaps typical of TUI projects in general?
(EDIT to strikeout) ~~The binary is also 53M as a result whereas /usr/sbin/tree is 80K on my machine -- not really a problem on today's storage, but very roughly 500-1000x different in size isn't nothing.~~
Maybe it's linking-related? I don't know how to check really.
(EDIT: many have pointed out that you can run `cargo build --release` with other options to get a much smaller binary. Thanks for teaching me!)
JoshTriplett
> The binary is also 53M
That's a debug binary, and the vast majority of that is debug symbols. A release build of this project is 4.3M, an order of magnitude smaller.
Also, compiling out the default features of the git2 crate eliminates several dependencies and reduces it further to 3.6M.
https://github.com/bgreenwell/lstr/pull/5
https://github.com/rust-lang/git2-rs/pull/1168
Stripping the binary further improves it to 2.9M, and some further optimizations get it to 2.2M without any compromise to performance. (You can get it smaller by optimizing for size, but I wouldn't recommend that unless you really do value size more than performance.)
esafak
No offense, but 4.3MB is huge for what it does. Most shells take less space than that! Where's all the bloat coming from?
o11c
For reference, some statically-linked shells on my system:
2288K /bin/bash-static (per manual, "too big and too slow")
1936K /bin/busybox-static (including tools not just the shell)
192K /usr/lib/klibc/bin/mksh
2456K zsh-static
For comparison, some dynamically-linked binaries (some old) 804K ./bin/bash-3.2
888K ./bin/bash-4.0
908K ./bin/bash-4.1
956K ./bin/bash-4.2
1016K ./bin/bash-4.3
1092K ./bin/bash-4.4
1176K ./bin/bash-5.0
1208K ./bin/bash-5.1
1236K /bin/bash (5.2)
124K /bin/dash
1448K /bin/ksh93 (fattest when excluding libc!)
292K /bin/mksh
144K /bin/posh
424K /bin/yash
848K /bin/zsh
(The reason I don't have static binaries handy is because they no longer run on modern systems. As long as you aren't using shitty libraries, dynamic binaries are more portable and reliable, contrary to internet "wisdom".)koito17
> Most shells take less space than that!
Most shells dynamically link to a runtime your OS provides "for free". The 4.3 MiB binary in question is bundling the Rust runtime and its dependencies.
For reference, a statically-compiled C++ "Hello, World" is 2.2 MiB after stripping.
% cat hello.nix
{
pkgs ? import <nixpkgs> { crossSystem = "aarch64-linux"; }
}:
pkgs.stdenv.mkDerivation {
name = "hello-static";
src = pkgs.writeText "hello.cpp" ''
#include <iostream>
int main() {
std::cout << "Hello, World!" << std::endl;
return 0;
}
'';
dontUnpack = true;
buildInputs = [ pkgs.glibc.static ];
buildPhase = "$CXX -std=c++17 -static -o hello $src";
installPhase = "mkdir -p $out/bin; cp hello $out/bin/";
}
% nix-build hello.nix
...
% wc -c result/bin/hello
2224640 result/bin/hello
JoshTriplett
Among the features it has: an interactive terminal GUI, threaded parallel directory walking, and git repository support. In around a thousand lines of code, total, including tests, half of which is the GUI.
have-a-break
I feel like that's just the result of having a native package manager making natural bloat and a compiler which hasn't had decades of work.
CGamesPlay
Likely needs features tuned. I compared Eza, similarly in Rust, and it's 1.6 MiB compiled. Looking at the Cargo.toml, it includes git2 with default-features = false. https://github.com/eza-community/eza/blob/main/Cargo.toml
pveierland
Building in release:
cargo build --release
du -sh ./target/release/lstr -> 4.4M
Building with other release options brings it down to 2.3M: [profile.release]
codegen-units = 1
opt-level = "s"
lto = true
panic = "abort"
strip = "symbols"
cyann
I did some benchmarks on one of our CLI and found that `opt-level = "z"` reduced the size from 2.68M to 2.28M, and shaved 10% on the build time, worth a try.
I'll try with `panic = "abort"` for our next release, thanks for the reminder.
fabrice_d
You are probably looking at a debug build. On Linux, a release build (cargo build -r) is ~4.3M, and down to ~3.5M once stripped. This could be reduced further with some tricks applied to the release build profile.
getcrunk
Great catch! Comments mentioned getting it down to ~2MB but that’s still humongous.
If you just think about how roughly (napkin math) 2MB can be 100k loc, that’s nuts
arlort
Is It though? You won't get it on an embedded device (maybe) but you could install a thousand of these tools and barely even notice the space being taken up on most machines
getcrunk
I think that’s a lame argument. First because it’s kind of a fallacy. Size is absolute not relative to something. Especially for software. No one thinks of software size primarily in the context of their disk space.
Further I think everyone keeps getting larger and larger memory because software keeps getting more and more bloated.
I remember when 64gb iPhone was more than enough (I don’t take pictures so just apps and data) Now my 128 is getting uncomfortable due to the os and app sizes. My next phone likely will be a 256
vlovich123
When you include the code for all the dependency features this uses, you probably do end up close to 100k LoC net, no?
ethan_smith
Try `cargo build --release --no-default-features` to get a much smaller binary (~5-10MB) - Rust statically links dependencies but supports conditional compilation for optional features.
aystatic
Glancing at the Cargo.toml, the package doesn't define any features anyways. `cargo b --no-default-features` only applies to the packages you're building, not their dependencies -- that would lead to very unpredictable behavior
sdegutis
So after writing this to learn Rust, what are your thoughts on Rust? What do you especially like and dislike about it, or what were you surprised about?
w108bmg
I appreciate the ecosystem of packages that seem really well maintained. I don’t love the syntax and find Rust harder to read and learn so far compared to something like golang (I’m used to R which is not a compiled language but has a great dev community).
I do love the compiler and support tools built into Cargo (fmt, clippy, etc.).
sdegutis
That's been similar to my experience. The ecosystem is extremely polished and smooth, the build tools and package manager and IDE support, all of it. Especially compared to C++ which I cuold barely get working here.
aystatic
Neat, looks like a combination of erdtree[0] and broot[1]. I use both on a daily basis, are there any features that stand out from the two?
[0]: https://github.com/solidiquis/erdtree [1]: https://github.com/Canop/broot
null
ironuchan
[dead]
be87581d
[dead]
Hi HN,
(First time poster!)
I'm the author of `lstr`. I've always loved the classic Linux `tree` command for its simplicity, but I often found myself wanting more modern features like interactivity and Git integration. So, I decided to build my own version in Rust with a philosophy of being fast, minimalist, and interactive. It was also an excuse to help learn more about Rust\!
Here's a quick look at the interactive mode:
https://raw.githubusercontent.com/bgreenwell/lstr/main/asset...
I've just released v0.2.0 with some features I think this community might find useful:
It also supports file-type icons (via Nerd Fonts), file sizes, permissions, and respects your `.gitignore`.The project is open-source and I would love to get your feedback.
GitHub: https://github.com/bgreenwell/lstr
Crates.io: https://crates.io/crates/lstr
Thanks for checking it out!