I find Rust extremely unreadable and can detail exactly how:

I know ripgrep is written in Rust and works really well and "does one thing", so I'll go to its repo ( https://github.com/BurntSushi/ripgrep ) and read through a bit. First, I'm looking for a "src" folder, but that does not exist, so right off the bat I am a little uncomfortable. Nothing in the root folder looks like it would be the source. What are "complete" and "scripts" and "pkg" folders? Because those (in that order) would be what I check.

"complete" looks like command line completion. The "scripts" directory has one file, "copy-examples". "pkg" contains folders "brew" and "windows". Still lost...

I thought "crates" was like modules for Python, so I think that would only contain dependencies and stay out of there.

Finally, I relent and open "build.rs" which I suspect is something like "build.ninja" and I can figure out which node has source files. Stupid me. "build.rs" IS the source file.

Oh. No, actually, it's not all of ripgrep, in fact. It's the tip of the iceberg. The source is in crates/core/app.rs, and build does manpage compilation, registers shell completions, etc.

So, line one. I don't know what the return value App<'static, 'static> is. I understand it's an App object, and the tick (no idea what it's called; this is starting to feel like a fracture mechanics lecture) I know has to do with ownership. It's tough to see how there are two subtypes in App<> when the assignment of app has ten functions that assign attributes. I can certainly READ what is being assigned to the App object. I wouldn't be able to WRITE any of this so far. Unimportant, it's not the meat and potatoes of ripgrep.

Now I'm scrolling, looking for something cool to analyze and run across Vec<&'static str>. Okay, I thought tick was ownership, but I also thought ampersand was ownership-related. Maybe one is mutability? (This is like knowing how to play music really well but now learning a totally foreign instrument.)

Skimming, it looks like this whole file is the argc/argv parser. I'll look for something interesting in main.rs, search.rs, and subject.rs (the last because it's an unusual name).

struct Config {
strip_dot_prefix: bool,
}

Well. I can read that! Next line...

impl Default for Config {
...

...not that. It's probably something like a class method.

As I'm reading through, it feels like a LOT of kicking the can down the road and verbosity. There's a function binary_detection_explicit(self, detection) that just assigns detection to self.config.binary_explicit and returns self. binary_detection_implicit() does basically the same with a different assignment member. getters and setters, roadblocking readability since the dawn of computer science... (a few lines later, the function has_match() returns the value of has_match. ugh. This is why I prefer crudely written C to textbook C++.)

I see .unwrap() in a line. This tripped me up during the tutorial; still no idea what it does. Sure I can look it up, but there are a hundred other terms to learn: unwind, map, Result (OH! The two App return types are probably Ok and Err ... maybe?), convert, concat, const, continue, ...

subject.rs, btw, can probably be written in three lines of really dense Python or ten lines of really well-written Python with a few other lines of Doxygen. Instead, it's about 90 lines of Rust with another 50 lines of comments.

Finally, I find something noteworthy.

fn search(...) ...
fn iter(...) ...
for subject in subjects {
searched = true;
let search_result = match searcher.search(&subject) {
....

This is all extremely readable and just feels like a standard queue in any language. But then there are lines like:

if let Some(ref mut stats) = stats {
*stats += search_result.stats().unwrap();
}

I give up. Are you assigning the variable stats to itself? by reference but mutable? If Rust doesn't have pointers, what is *stats? unwrap() is no longer the most confusing part of this.

-----

Postface, I'm originally a mechanical engineer who eventually got roped into writing driver software, so C is my comfort zone and I don't need anything much more complicated than "set bits here, read registers, use a 40-year-old communication protocol, handle errors".

There's simply no easy analog between Rust and Python ... or Rust and C ... or Rust and SQL ... or Rust and PRACTICE ... or Rust and any other language I've learned. I understand a lot of the CWE Top 25 software errors are mitigated by Rust and it's not just a theoretically correct but unusable language, but there hasn't been a strong reason to learn it, and the learning curve is made steeper by preconceived notions of every keyword.

-----

I forgot to compare with grep.c

Extremely readable. I know right away which part is args, where memory allocation happens, where file handling occurs, and then the "hard part"---where the magic happens---is the last page on my monitor. I can just stare at that and mentally deconstruct until pieces fall into place... Look up regcomp()? check. Look up grep_tree()? check. Read the kernel.org discussion on why grep_tree() was written and how it relates to ensure_full_index(), which explains some of the other variables.

The whole thing sits nicely in a few pages with terse but clear comments (unlike this post) and looks familiar even though I've never seen the source before.

-----

Last edit. I tried just now to refresh on .unwrap(). https://doc.rust-lang.org/rust-by-example/error/option_unwra...

Only because of the comments did I realize that calling drink.unwrap() will panic (throw an error?) if that argument is empty. So, unwrap is just the "if (... == nullptr) { return ...; }" of Rust, it seems.

But it's somehow an alternative to .expect() and has its own alternatives, unwrap_or/unwrap_or_else/unwrap_or_default, and it also seems optional. And my time in the rabbit hole is over; my children need to go to bed.