7 ms·
People keep saying this and I just do not get it. It's just… not that bad?
by stouset 4y ago
People keep saying this and I just do not get it. It's just… not that bad?
- ok123456 4y agoThe core language is fine, but when you actually start building things you need to introduce lifetimes, all the traits that you polluted your interface with and then add on async, it gets out-of-hand quickly.
- dimitrios1 4y agoThe top comment gave a perfect example: #![feature(strict_provenance)] These feature enablement blocks drive me crazy. You could go from codebase to codebase and it's almost like you are working in a different language depending on how many of these are enabled or not. I've been trying rust on and off since it's release, and I still have yet to feel like I have a grasp on some "core" subset of the language I can fall back on to solve most of my problems. I always have to scour documentation for the hot new thing to turn on or do, and this isn't to scorn innovation and change, but it does get exhausting at some point. (Please release a spec)
- satvikpendem 4y agoThose feature blocks exist only for nightly/unstable Rust. You don't have those on stable Rust. The most you might have are derive blocks but those just automate what you'd write by hand anyway.
- deleted 4y ago[deleted]
- Rusky 4y agoA spec is not a replacement for #![feature] attributes. It's rather the opposite: #![feature] indicates that you are stepping out of the stable, specified core language and into an area that is still a work in progress and thus cannot have a committed specification yet. You shouldn't need it at all unless you are actively experimenting with some unfinished proposal.
- kaba0 4y agoI think it is just inherent complexity in the low-level space. (Though I do agree that Rust definitely moves fast, which is sort of understandable as a relatively newish language)
- pie_flavor 4y agoIt's not about actual ugliness, it's just something that is hard to articulate as something other than ugliness. https://matklad.github.io/2023/01/26/rusts-ugly-syntax.html https://matklad.github.io/2023/01/26/rusts-ugly-syntax.html
- DonaldPShimoda 4y agoIt's amusing to me that many of the people who complain about the aesthetics of Rust's syntax are quick to also say bad things about, like, Haskell, or Lisps, or other languages with comparatively low syntactic overhead. I think the thing people don't like about Rust is that it looks vaguely C-like but is clearly not C. People might like it better if it was further removed (aesthetically) from C's syntax. But then they would also complain. I think there was no way for Rust to meet all its semantic goals and also make people happy about the syntax.
- flohofwoe 4y agoTypescript and Zig are two other languages that look vaguely C-like (even though they are on two opposite ends of "C like"), but both are a whole lot easier on the eyes than most Rust code. C++ on the other hand also is vaguely C like, but can look equally messy as typical Rust code. OTH I find Makepad's Rust style very readable, but I can't quite put my finger on it what's different from other Rust code bases: https://github.com/makepad/makepad https://github.com/makepad/makepad
- throwawaymaths 4y agoLanguages on the two ends of the spectrum are "not that great", IMO. Lisp is awful because even though there are very few greeblies, it introduces a TON of cognitive overhead, because you need to be constantly thinking about what something is, because the layout is TOO uniform. Rust on the other hand has a lot of greeblies and you have to remember what it is and what they do. For example macro attributes (and all their hidden effects) as well as the turbofish. Even some things like -> for the function body are simply unnecessary.
- foldr 4y ago
- malcolmstill 4y agoThere are a number of sources of noise in rust, but the one I find most annoying (because it's also so common) is the double colon. My current theory is that it's because the colon is the same height as lowercase letters. If you end up with a long::run::of::module::names, I find it all just blurs into one.
- coderedart 4y agocan you please point out a few more annoyances in rust syntax? I am a newbie playing with language design and could use some perspective :)
- malcolmstill 4y agoTo be honest that's the major one for me. Someone else has mentioned lifetime annotations, but those uncommon enough that I don't find it much of an issue. If I was to think about it: comparing rust to zig, zig benefits a lot from error and optional types having their own syntax rather than being treated like any other type. For example a return type of: Result<Option<usize>, Error>> in rust is rendered (mostly) equivalently in zig as !?usize Note: there are some options in rust for cleaning up errors such as the anyhow library. Disclaimer: I'm a zig fan and contribute monetarily so please weight anything I say on zig vs rust with that in mind. (I do write rust at work though)
- dureuill 4y agoi'm curious, how do you specify the error type in the zig version?
- malcolmstill 4y agoSo usually you don't have to specify the error type. The Zig compiler works out what errors are returned from the function by looking at any errors returned directly or errors returned from other functions called within the body of the function. That set of errors forms an enum that is the actual error type, you just don't have to write that out explicitly. An example might be: fn someFunction(a: usize) !usize { if (a < 10) return error.LessThanTen; const b = try anotherFunction(a); return 2 * b; } fn anotherFunction(x: usize) !usize { if (x < 20) return error.LessThanTwenty; return x * 3; } The compiler infers the error type of `someFunction` as: error { LessThanTen, LessThanTwenty } When you then `switch` on an error type, the compiler will exhaustively check that you have handled all the cases. Note the "(mostly) equivalently" was a reference to the fact that Zig errors can't (currently) contain any other information, whereas an error in rust can carry other information. Also note that the compiler can't infer the error type in all case, for example in the case of a recursive function. In that case you do need to explicitly write out the error set.
- jcranmer 4y agoI suspect the things that people are reacting to are a mixture of the following: * Types in Rust use prefixing to create derived types whereas C family languages generally use postfixing. A pointer is i32, not int; an array [i32; 5], not int[5], etc. This makes special characters appear more heavily at the beginning of the scan line, and probably makes them slightly more noticeable as a result. * Lifetimes have the form 'a, and that single quote is likely to bother a lot of people (I know it bothers me). * Unqualified name lookup in Rust is a bit weaker than other languages, which makes the scope operator (::) more common. * Passing in explicit generic type arguments for a function requires an extra :: for seemingly no reason. * Similarly, macros require an explicit ! in the name to invoke. Given that println! (and formatting in general) is a macro and not a function, this means you get a lot of extra uses of ! that's unexpected for C family code. * Again, the try operator (also decently common) is another random special character. * Attributes also use #[] syntax, or sometimes #![]. While C++11 did use [[]] to designate its attributes, it's also something that always felt a bit ugly to me personally (I find the @Decorator() pattern from Java or Python to be a visually cleaner way to do attributes). In short, Rust generally has a higher density of special characters than other C family languages, and I think that contributes to a sense of ugliness.
- q3k 4y ago> * Passing in explicit generic type arguments for a function requires an extra :: for seemingly no reason. There is a very good reason for this. Behold, the Bastion of the Turbofish. https://github.com/rust-lang/rust/blob/master/tests/ui/parser/bastion-of-the-turbofish.rs https://github.com/rust-lang/rust/blob/master/tests/ui/parse...
- loeg 4y agoThat is not a good reason as far as end users are concerned.
- Asooka 4y agoMy biggest peeve is types on the right. I would say that C is "humanistic" in its type declarations, while Rust and the rest of Pascal's lineage are "mechanistic". Concretely, look at this: int foo(int a, float b); vs fn foo(a: i32, b: f32) -> i32; In Rust's case you're specifying to the machine what the thing is, i.e. "I am declaring a function called foo. The function has a first parameter called a, of type i32, ... The function returns an int". Whereas in C you have a "declaration follows usage" idiom, such that what you're saying is "typing foo(a, b) produces an int on the left side, within foo a produces an int lvalue, ...". The function arguments flow from right to left and the result emerges on the left with the given type. The order of tokens in declaring foo matches the order of tokens when calling foo. It also matches the order of importance - first is the return type, then the function name, then each parameter type followed (optionally) by the parameter name. Or look at an array declaration: int arr[5]; "You get an int when you type arr[N] where N is less than 5". This kind of argument of course goes all the way back to AT&T vs Intel assembly syntax and I am firmly on Intel's side.
- __jem 4y agoThis isn’t a Rust thing. Lot’s of new language have the type declaration after the variable because of type inference which makes such declarations optional.
- todd8 4y agoAlgol 60, Algol W, and Algol 68 were influential in the historical development of subsequent programming languages. C gets its type on the left order from the Algol family. Pascal, Modula, and Ada are all type on the right languages and were also very influential in the historical development of programming languages. I happen to prefer types on the right for complex declarations. Note that Pascal and Modula were designed by Niklaus Wirth after he created Algol W; it seems he preferred types on the right.
- stouset 4y agoI'll simply say that you are the first person I've seen come to the defense of C's inside-out variable syntax.
- alphanullmeric 4y agoNo arrow operator, no default args, no named parameters, no structure defaults and instead the incredibly verbose ..Default::default() + a trait impl as a substitute, no variadics, overall weak generics compared to C++ and a huge reliance on macros, etc etc. Rust programmers address this by calling everything rust does poorly an antipattern, ie. the “why would you do that” card.
- tialaramex 4y agoThe arrow operator is just a terrible idea, and it's weird that people defend it. It makes sense that C did this, it was a long time ago and compilers weren't very smart so C needs to make up for that, in C++ it's just carried over from C. The absence of default args is a deliberate choice, notice that Rust does have default type arguments in polymorphism, the absence of defaults for function parameters -- which would be technically easy to implement -- reflects a belief which I've come to agree with that overloading is a bad idea, and defaults most often in practice mean you're overloading. For example, C++ std::ranges::binary_search uses defaults to present what are in effect at least two distinct features, as a single function, suiting C++ sensibilities, whereas Rust reflect almost the same capabilities as three functions []::binary_search []::binary_search_by and []::binary_search_by_key For a very simple binary search, things seem pretty similar. In Rust we have a single parameter, for our searched-for element, and in C++ we can stop after that parameter, leaving the comparison function and projection as default for similar effect. However for binary_search_by the C++ is contorted by this API shape. Instead of a callable to decide whether our search found what it was looking for, and if not where it is relative to the searched-for element, the C++ is obliged to carry that element (because it was an earlier parameter) even if it's unused - and then a comparison function which takes the element, and only then optionally a projection which you may or may not use. And for binary_search_by_key the C++ is even more awkward, we have a good reason to use a value here, but we're obliged to specify the comparison function even though we only want to write a callable to make suitable keys (ie a projection), because of the order of the parameters. These would be better served, as in Rust, by three distinct functions, with only the appropriate parameters for each function - even if you choose to actually implement the simplest in terms of the others, because of the documentation and the API shape afforded if you think about it as three things not one with defaults carefully tailored to allow all three uses. Variadics is a genuinely useful feature, but to do it properly is very difficult, C++ 98 doesn't have anything better than Rust [C compatibility, with no real type checking], C++ 11 does have the outline of what you'd actually want, and C++ 17 has much closer to what I'd want to see in Rust some day. Much of what you're thinking of in "weak generics" is probably deliberate constraints to only allow coherent things, in C++ they don't care if you want to make a Foo<NaN> even though that's nonsense, IFNDR gives them the ultimate out, your program has no defined meaning, so too bad. There are some obvious things Rust wants to have but doesn't yet in this space, including broader const Generics (e.g. my OnewayEqual ought to be Oneway<Ordering::Equal>) but it's not going to pursue the irrational C++ exuberance because it's so quickly unsound. C++ doesn't care about that while Rust does.