6 ms·
Rust would be so much easier for me if it didn't feel like every. single. keyword. was different than my daily drivers, Python and C---from type names to standa
by PennRobotics 3y ago
Rust would be so much easier for me if it didn't feel like every. single. keyword. was different than my daily drivers, Python and C---from type names to standard library functions, containers to punctuation. Even "void" isn't safe (hehe pun) from this keyword purge.
Beginners, count your blessings. You don't need to unlearn other languages before learning Rust.
I sincerely believe the only keywords common to Rust and C are a few control flow keywords (for, if, while; but not switch, goto, or do-while) and "bool"---the latter only because of C99 and stdbool.h
- silvestrov 3y agoreadability is so underrated. I think readability is a major part why java was hugely successful: C programmers could read java code and immediately understand what the code did and able to make minor modifications to the code. This is very difficult with Rust code. Rust "learned" too much from Perl in this regard.
- tialaramex 3y agoDo you think so ? Maybe I've been immersed in Rust for too long but it doesn't seem difficult to read to me. Here's Barry Revzin, a C++ programmer, explaining what he wants with a Rust example https://brevzin.github.io/c++/2020/12/01/tag-invoke/#this-isnt-better-enough https://brevzin.github.io/c++/2020/12/01/tag-invoke/#this-is... Now like I said, I'm a Rust programmer, so of course I understand PartialEq's definition there, but, is it really hard to see what's going on as a C programmer? There's a lot of magic you don't have, you don't have traits, or references, or the Self type or the self keyword, or really any of the mechanics here, and yet it seems pretty obvious what's going on, doesn't it? I literally don't remember any unpleasant surprises of this form when learning Rust. Actually mostly the opposite, especially for != I thought, from experience with other languages like Java, C++ or Python, OK obviously at some point the other shoe drops and they show me that I need a deep comparison feature. Nope, Rust's comparison operators are for comparing things, it isn't used to ask about some surface programmer implementation detail like the address of an object in memory unless you specifically ask for that. If Goose implements PartialEq, so that we can ask if one Goose is the same as another Goose, then HashSet<Goose> also implements PartialEq, so we can ask if this set of geese is the same as another, and HashMap<Goose,String> likewise, so we can ask if these two maps from geese to strings are mapping the same geese to the same strings.
- PennRobotics 3y agoI 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.
- raincole 3y ago> C programmers could read java code and immediately understand Uh... maybe? But Rust is more like a replacement to C/C++. So a better question is that whether C#/Java programmers could read C/C++ code and immediately understand those & and *.
- silvestrov 3y agoNo. C existed long time before java, so the natural career progression was for C programmers to become java programmers. It is not at all as easy for C programmers to become Rust programmers as you need to learn a lot about Rust before you are able to understand the Rust code.
- Const-me 3y ago> whether C#/Java programmers could read C/C++ code and immediately understand those & and *. Just like Rust, C# has an unsafe superset of the language. The meaning of all these * in unsafe C# exactly matches C.
- pjc50 3y ago> C programmers could read java code This isn't some abstract "readability", though, this is "how close are your langauges in the evolutionary tree". Where ALGOL is the proto-indo-european of quite a lot of langauges, but not all.
- tialaramex 3y agoConveniently we can actually compare: https://doc.rust-lang.org/reference/keywords.html https://doc.rust-lang.org/reference/keywords.html https://en.cppreference.com/w/cpp/keyword https://en.cppreference.com/w/cpp/keyword And they're even both provided in (mostly) alphabetical order, which makes this easier for humans Rust doesn't reserve type names as keywords, which seems like an eccentric choice in C++ (e.g. wchar_t is a keyword!) although perhaps given how fraught parsing is in C++ they had no choice. The common keywords are: break, const, continue, else, enum, extern, false, for, if, return, static, struct, true, union and while. true and false are just boolean values, although given C++ like many languages considers "false" to be true, and 0 to be false, I guess it's a surprise they bothered making these keywords. const, enum, union, static, and struct are about types and values. The choice to have "const" mean "immutable" not "constant" in C++ is hilarious, yeah that's going to cause wider issues in how your programmers understand software. I think C++ enum is rather sad, here's a whole kind of type and it's basically abandoned, it's not allowed to have features the other types have, for no discernible reason. Rust's enum really shows how much opportunity was wasted there. break, continue, else, for, if, return and while are indeed control flow keywords, although what they do in Rust varies quite a lot from C++. Rust's for is a for-each, it consumes an Iterator (literally, it's a syntax sugar for a loop which uses IntoInterator::into_iter and breaks when the iterator is exhausted) and Rust's break can break-label-value, which is a violently different thing than the C++ break even though the obvious beginner syntax looks identical. try, do and virtual are reserved in Rust but are not currently used (in stable Rust) to mean anything, we can expect that try is likely to some day be used for a related but different purpose to its cousin in C++.
- hardlianotion 3y agoWhat's the difference between constant and immutable?
- layer8 3y ago“Const” doesn’t mean immutable in C++, in my book. It means unmodifiable or read-only. Immutable means the value won’t ever change, whereas unmodifiable means that the value can’t be modified via that type (but it might be modified some other way).
- nayuki 3y agoVoid is not a good example for you to point out. Rust's interpretation is correct: The unit type () is what most functions should return. The void type has no valid values at all, which means it cannot be returned. It is other languages that are wrong for using the term void. The actual void in Rust is called never: https://doc.rust-lang.org/std/primitive.never.html https://doc.rust-lang.org/std/primitive.never.html .
- saurik 3y ago> The void type has no valid values at all, which means it cannot be returned. In C/C++, any value can be cast to void, which should be sensible as any pointer can decay to a pointer to void. Thereby, the Wikipedia definition of the void type even says it is like a unit type, and the page for the unit type notes 1) the void keyword simulates a unit type and 2) that--and I think this is very important, to get into the mindset of this term--that the actual unit object type in Java is named Void. It does really really suck that you can't declare a variable of type void and that some compilers decided the size was 1 for long enough that the size is now undefined (instead of 0), as these mistakes lead to a ton of ridiculous code duplication in C++ templates. (This would be the first thing on my list of things to fix if I were allowed to make any arbitrary breaking change to C++.) But like, it seems very wrong to claim it has no values at all: in C++, if you have a function that returns void, you can even call another function that returns void and use the return keyword oh whatever you consider it to have returned. It clearly has some kind of value in many contexts--and, despite having a size that is undefined (and potentially 1), that value carries no state--so there is only one possible value: it is merely the term people use for unit in this kind of language. (To be clear, though: I am not at all claiming that Rust is wrong for using () or unit or whatever, as there is a ton of history of using such in other safe academic languages and it certainly doesn't confuse someone like me. I do, though, think that its mission isn't well served by not at least trying to look as much like the specific languages that need to be replaced--C and C++--as possible.)
- mr_00ff00 3y agoI feel like this is a bit unfair. Rust takes most of its names from C++ or OCaml. You just are exposed to different languages than what was used as the base.
- PennRobotics 3y agoIt's also the concepts. The comment from fauigerzigerk sums it up well. You just have to know via experience, a great teacher, or very attentive learning that vec![1,2,3] is going to pop all of its values in a "print elements" loop while [1,2,3] will retain each value. Other languages have similar behavior, but it's often more obvious e.g. Python with the .next() keyword.