11 ms·
Rust has evolved from something safe and simple (once you grasp the different type of pointers) to something way less accessible. Rust source code is now full
by jedisct1 7y ago
Rust has evolved from something safe and simple (once you grasp the different type of pointers) to something way less accessible.
Rust source code is now full of annotations, and very hard to read and maintain.
Traits have evolved from a nice feature to something overused everywhere.
Basic crates use so many generics, impl Trait and abstractions over abstractions over abstractions that it's really difficult to follow what concrete types you need to satisfy these.
When one of the oldest and still unanswered issue in the Hyper HTTP library is "how do I read the body?", there is a conceptual problem.
And things got even more complicated with Futures. async/await don't solve much, as besides textbook examples, these are unusable without deep knowledge of how everything works internally. Pin/Unpin makes things even more complicated.
I'm an early Rust adopter and advocate. However, I wouldn't consider it any longer for new projects.
My productivity in Rust has become very low compared to other languages such as Go or even C, that let me easily express what I want to do.
With Rust, 90% of my time is spent trying to understand how to express what I want to do. Such as what types I have, what 3rd party crates expect and how to convert them.
- oconnor663 7y ago> something way less accessible This is subjective, but there are at least a couple big counterexamples. Non-lexical lifetimes are a huge benefit for newcomers. I've gone through my old projects and just deleted all the nested blocks that I used to need to satisfy the borrow checker. Everything just works now. Also being able to match on `&T` or `&mut T` without putting the `ref` and `ref mut` keywords in each non-`Copy` binding, is another pretty big win. That's an entire keyword that we no longer need to teach to newcomers. > Basic crates use so many generics, impl Trait and abstractions over abstractions over abstractions that it's really difficult to follow what concrete types you need to satisfy these. Again this is subjective, but not following concrete types it the entire point of `impl Trait`. The type of `foo.iter().map(...).filter(...).step(...).take(...)` is a monster. Life is better when we don't have to type it. There are also new capabilities that come along with it: APIs don't have to commit to returning a certain kind of unboxed iterator, and unboxed closures can now be returned. > async/await don't solve much, as besides textbook examples See the real code examples here: https://docs.rs/dtolnay/0.0.3/dtolnay/macro._01__await_a_minute.html https://docs.rs/dtolnay/0.0.3/dtolnay/macro._01__await_a_min...
- hathawsh 7y agoIs there a way to ask Rust to tell me the type of an expression like `foo.iter().map(...).filter(...).step(...).take(...)`? I often feel very much in the dark about Rust types and some kind of command to show me Rust's interpretation of the type of an expression in context might help a lot.
- oconnor663 7y agoThe hack that I've seen people use the most is to write a line like this: () = foo; Then rustc will print out an error that includes the type of `foo`.
- hathawsh 7y agoNice, thanks!
- danbolt 7y agoScott Meyers also suggests the same sort of thing in Effective Modern C++ when not using an IDE. Personally, I use the technique a lot for debugging template errors in the language.
- stochastic_monk 7y agoSpecifically: template<typename T> struct TD; Instantiate this as ‘TD<decltype(foo)>’ to get the full name of the type. Rust’s does seem more concise.
- edflsafoiewq 7y agoNote that you don't get the full name of the type with a bad assignment in Rust, you only get the part of the type that was deduced before type checking failed. A generic type might have undeduced blanks, eg. Vec<_>. A side-effect of type inference.
- pc2g4d 7y ago
- rapsey 7y agoThere definitely is a problem with some libraries using so many abstractions (traits) you have no idea how to use the thing. I however do find writing rust to be quite enjoyable. But I have so far entirely avoided the futures ecosystem and consider it a very good decision. One needs to have a very critical eye on what libraries to use
- amedvednikov 7y agoI'm working on a language with similar goals, but with the focus on simplicity: https://vlang.io https://vlang.io A relatively stable 0.2 release will be out this month.
- oconnor663 7y agoI'm curious about the example code at https://vlang.io/compare https://vlang.io/compare (apologies if you answered this before): - Does the compiler use escape analysis to determine that the `cursor` variable needs to be heap allocated? If not, how does it know that the worker threads won't outlive the stack frame that owns `cursor`? If so, when does the allocation get freed? And does implicit heap allocation conflict with the goal of being as fast as C? - How does the compiler know that the `lock` block is the only code that touches `cursor`? Does it still work if the `lock` block is in a subroutine? Does that require whole-program analysis? If two different `lock` blocks touch the same variable, are they implicitly a single `lock`?
- kbd 7y agoThe two languages I'd be most interested in seeing added to that comparison are Nim and Zig. Those definitely have most of my mindshare on "new low-level/efficient languages simpler than Rust".
- kzrdude 7y agoSounds very interesting. I guess you should compare compile speeds with D and Nim, since they are also interested in fast build times.
- koszikm 7y agoIt looks nice, but I'd add to the comparison table that Erlang has both hot code reloading and no global state.
- digikata 7y agoAre you trying to do data lifetime analysis in V? I don't see that listed and to me it's pretty much Rust's key feature to enabling a high degree of memory leak and multithreading safety.
- staticassertion 7y agoNone of what you're describing really tracks for me, having written many thousands of lines of rust and interfacing with my third party crates. "async/await doesn't solve much" like your whole post confuses me. I think right now a lot of rust libraries are "hyper generic" because they're intended to be lower level and to be built on top of. Like hyper, as an example. I've never found it particularly hard to work with generic libraries though.
- jnordwick 7y agoAsync+Await+Futures isn't needed at all, performs worse than a basic epoll loop, is more confusing, and less able to handle complex flow. But they add a lot of complexity. The futures paragidm makes the hard stuff almost impossible to do efficiently. It took c++ years to blow its complexity budge. Rust, "Hold my beer."
- staticassertion 7y ago> Async+Await+Futures isn't needed at all Define "needed" ? Code with async/await is radically simpler compared to without - this is not just basic sugar but really, really important due to rust's borrow semantics There is code I have written that required Rc/RefCell ceremony to get around the borrowchecker that will be completely handled by async/await. You can read more about this here: https://aturon.github.io/tech/2018/04/24/async-borrowing/ https://aturon.github.io/tech/2018/04/24/async-borrowing/ > performs worse than a basic epoll loop There are open issues and afaik all of these are solvable problems with known/ theorized solutions. > is more confusing, and less able to handle complex flow Sorry, that's absolutely just not the case Even if "confusing" is subjective, you can not tell me that writing complex loops/ control flow is easier in futures than in async/await. Just look at the examples here: https://docs.rs/dtolnay/0.0.4/dtolnay/macro._01__await_a_minute.html https://docs.rs/dtolnay/0.0.4/dtolnay/macro._01__await_a_min...
- woah 7y agoThere are programmers who mostly work on code that does a lot of work with the CPU, and needs to be multi core. For these applications, async/await has little to offer. People who are puzzled by async/await often fall into this camp.
- Dowwie 7y agoYou are entitled to your own opinions but not your own facts. Rust was never simple. :)
- excursionist 7y agoI would argue it was, and still is, simpler than modern C++.
- deleted 7y ago[deleted]
- kirubakaran 7y agoIsn't everything simpler than modern C++?
- TheDong 7y agoHaskell would beg to differ. Arguably, all dynamically typed languages are also more complex because there are huge classes of programs that are incorrect which simply would not even compile as valid programs in C++. If complexity is based on how likely a syntactically correct program is also logically correct, then type systems remove a lot of complexity.
- dragonwriter 7y ago> Arguably, all dynamically typed languages are also more complex because there are huge classes of programs that are incorrect which simply would not even compile as valid programs in C++. There are no programs (including ones that produce runtime failures) in a dynamically-typed language that cannot be duplicated in C++. There are certainly dynamic-typed programs wthat will fail at runtime where the natural way to attempt to express the same idea in C++ would result in a compile time error, just as there are dynamic-typed programs that will operate correctly that are harder to express in C++ because you either have to do type gymnastics to convince the compiler of their correctness, or evade the compiler by building an interpreter for a dynamic-typed language, to do it in C++. This is a difference in where the complexity in each language lies, not the overall complexity.
- foldr 7y agoI get the feeling that Rust is going to become, like C++ and Haskell, a language that you have to "live in". That is, if you take a break from the language for a few years, you won't understand the code that people are writing when you come back. Contrast that to, say, Go or Python, where you can come back to the language after five or ten years without any major difficulty.
- pjmlp 7y agoGo, for sure. Python, knowing it since version 1.6, I doubt it very much.
- ausjke 7y agopython 3x seems is a milestone, and relatively speaking, python is very readable
- pjmlp 7y agoPython is very powerful. Reflection, meta-classes, decorators, comprehensions, slots, operator overloading, multiple inheritance and plenty of other features, some of them with semantics that changed across minor versions. Python the full language, is at the same capability level as C++. Many just don't realize it, because it is kind of targeted to teach programming and then people move on.
- slx26 7y agoI'd say that's something Python does well. Many people don't realize because they don't need those features. If you can do with simple code and patterns, go with that. Rust on the other hand, hits you with all the complexity from the start (or a lot of it). I'm not saying this is necessarily bad, or that having a lot of "hidden complexity" is always good, the use-cases are different, but in many cases it's kinda good to not need to know about the things you don't need to use.
- sitkack 7y agoAnd the level of mutation one can inflict upon the runtime is magnificent! http://farmdev.com/src/secrets/framehack/ http://farmdev.com/src/secrets/framehack/ https://github.com/lihaoyi/macropy https://github.com/lihaoyi/macropy
- lukebitts 7y agoWow, hard disagree for me! I feel like Rust is the easiest language to maintain that I have ever used. I'm never afraid of changing things around since the compiler will always catch my mistakes and it is impossible (without unsafe) to introduce undefined behaviour. Whenever I'm using Rust my confidence levels are pretty much 100%. Having said that I don't know how the situation is for web dev since I'm a game dev, do you think that's the reason we have such different experiences?
- Dowwie 7y agoI'm using Rust full time for a lot of web dev. Using web frameworks and http clients hasn't been too painful, but asyncio web dev gets hairy because of the rules and syntax governing futures 0.1. Database dev using rust-postgres and extending it using its ToSql/FromSql traits has been very straightforward. Reading through the underlying source of these web frameworks / clients does involve medium/high difficulty, to varying degrees. Excessive use of generics ought to be curtailed, and it does but that seems to happen further down the pipeline as refactoring for readability plays a more important role. I can empathize with the OP, who has struggled to understand code written by others. It's not an issue just for Rust, though. Excessive use of object inheritance does no one any favors -- pick any language that features it and you'll find inheritance hell. Go also seems to have its own category of readability issues that people aren't being forthright about.
- aleden 7y ago> When one of the oldest and still unanswered issue in the Hyper HTTP library is "how do I read the body?", there is a conceptual problem. Is https://github.com/hyperium/hyper/issues/1137 https://github.com/hyperium/hyper/issues/1137 the issue you are referencing?
- hermanradtke 7y agoI think it is. I answered it, along with others. But hyper is changing a lot so the question is asked again. Strangely, the OP says async/await won’t help with things but in this case it would be a big help!
- mslm 7y agoThis is precisely why I've lost interest in Rust. It feels limiting in the same way that fat frameworks do - it just becomes so much harder to express what I _really_ want to do sometimes, because I'm always working around the limits and requirements of the framework/abstractions which a lot of the time won't map very well to my solution.
- jihadjihad 7y agoWhat do you find is the solution to this problem? How do you remain productive? Genuinely curious since I am merely a Rust dilettante
- zamadatix 7y agoGenerally choose something "boring" instead, whether it be libraries or language choice.
- vbezhenar 7y agoI can share similar experience. When I started to use Java, I happily digged into collections and stuff. It was quite easy to read and follow. Now with Java 8 I tried to read and follow their streams implementation. That was very hard. They implemented streams for semi-automatic parallelization which I never used and won't ever use and that made otherwise simple concepts just unreadable and unbearable. May be Rust developers tried to cover too big spectrum of the problems at once.
- nineteen999 7y ago> My productivity in Rust has become very low compared to other languages such as Go or even C, that let me easily express what I want to do. As a C/C++ programmer who has being following Rust from a distance for sometime, with an intention to learn it later, this is somewhat concerning to hear. I've been meaning to start learning it for some time, however, my current workload doesn't provide enough spare time to start absorbing it. In addition, it seems to be somewhat hard to find detailed information on how to do low-level things that would be very easy in C. Just as an example, one of our simulations relies on being able to craft raw UDP packets in order to "impersonate" remote basestations in our lab environment. While this is trivial to do in C or C++, I haven't been able to find a good reference on how to do that with Rust. The lack of accessible documentation means that I'm unlikely to consider writing some of these more security sensitive parts of our system in Rust, even though there may be net benefits to doing so.
- burntsushi 7y agoThat's just one take, and I very much do not agree with it personally. My productivity has only gone up as the tooling and language have improved. One thing that I am working on figuring right now is the proliferation of dependencies. But that's a"nice" problem to have. > Just as an example, one of our simulations relies on being able to craft raw UDP packets in order to "impersonate" remote basestations in our lab environment. I would assume you would do it in Rust just like you would in C. Where's the hang up?
- nineteen999 7y ago> I would assume you would do it in Rust just like you would in C. Where's the hang up? Why assume? If you know it can be done, why wouldn't you provide a pointer to an example? I've done my Google searching and can't find one. There are endless examples of this for C online, I'm just stating that with a cursory search I couldn't find one for Rust. The hang up is clearly at my end, I'm currently time poor, so now is not a good time for me to be learning a new language where I can't find good examples for what I'd use it for. Admittedly I need to start at "Hello World" with Rust anyway before I get as far as crafting raw UDP packets. The point I'm getting at is if I didn't know how to program in either C or Rust, I'd pick C for this problem right now because there are clear examples. That's all.
- fetbaffe 7y ago"Go is the result of C programmers designing a new programming language, and Rust is the result of C++ programmers designing a new programming language" https://drewdevault.com/2019/03/25/Rust-is-not-a-good-C-replacement.html https://drewdevault.com/2019/03/25/Rust-is-not-a-good-C-repl...
- csomar 7y agoThat's true. But you have to understand that Rust doesn't complicate things: Things are complicated. The difference between Rust and say, Python, is that Python hides lots of these complexities for you. This can have two sides effects: Performance and unintended consequences (bugs/hard to refactor/etc...) Rust instead puts all of these complexities in front of you: Deal with it now. So you have a choice: Deal with it now. Or deal with it later. And Rust has picked the former.
- pornel 7y agoThis is nonsense. Rust hasn't changed much since 1.0, but has matured a lot, and numerous small usability fixes add up, making it much nicer and productive than it was. Rust is simpler to use, and simpler to learn than it ever was. It has removed need for common pointless annotations (like ref in patterns, extern crate). Ambiguity of static vs dynamic dispatch in traits has been clarified with `dyn` and `impl` keywords. The const generics feature is going to obsolete the worst-of-the-worst abuses of the type system trying to emulate that feature, and remove gotchas around arrays. Async/await enables use of borrowed types in async code (freeing users from having to know about the `Pin`/`Unpin` implementation detail). Allows use of regular error handling, instead of requiring users to keep track of error types at each step in the chain of futures. It allows normal control flow instead of arcane wrangling with `Either` and boxing. You listed a bunch of problems, and at the same complained that Rust has been solving them!