4 ms·
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 re
by PennRobotics 3y ago
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 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_unwrap.html 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.
- thenewwazoo 3y agoI've been leaning Spanish for about the last six years. I'm reasonably effective at it. Not fluent, but I can speak and understand it well enough that I often don't notice how easy it's gotten for me. A while back, I tried reading something written in German and was actually surprised at how little I understood. Clearly I can understand not-English, why was German so hard? I spent a little while trying to parse out each word, guess common roots, vaguely remember the little bits of German grammar I've absorbed over the years, and so on. It was illuminating. I could not just force myself to understand German by relying on my success learning Spanish. (This is a real story, btw. I really did this.) This comment reminds me very strongly of myself, trying to read German. You know C, why shouldn't you be able to understand Rust? But they're not the same thing, and they communicate different things in different ways. There are concepts that simply do not map directly. Someone could very easily write the exact same post from the perspective of a Rust programmer starting with C. Where is the equivalent to Cargo.toml? What is this Makefile thing, and why doesn't it look like a configuration file? Etc. No amount of just forcing yourself to try is going to provide clarity unless you already understand the idioms and structure at play. With that said, I have written embedded Rust and it is an amazingly freeing experience. Writing C right now feels like trying to balance a knife on my finger. With Rust I can just get work done. I strongly recommend you spend the time to learn it. Even if it doesn't convince you that C's days are numbered, it will make you a better programmer by training you to think about safety and correctness up front.
- PennRobotics 3y agoMy mom was a Spanish teacher in a reasonably hispanic part of Florida. I'm now a permanent resident of Germany. Your example resonates. ----- I tried Embedded Rust about 4 to 6 years ago and found the experience underwhelming, as I was installing a TON of packages just to get Blinky and seeming to bypass a lot of standard Rust (no_std, no_main, use panic_halt as _, everything GPIO is unsafe, etc). The last straw was that imported Cargo packages had their absolute path somehow hardcoded in the symbol table, so my debugger would try to load, like... C:\Users\jacob\stm32-rust-thingy\app\solve.rs (and fail, because I'm not jacob and don't have his source code) I'll give it another shot. I'm sure the "many eyes" principle (and better/more tutorials in the past few years) make the process easier now than then.