Readit News logoReadit News
dang · 4 years ago
Why asynchronous Rust doesn't work - https://news.ycombinator.com/item?id=26406989 - March 2021 (482 comments)
kccqzy · 4 years ago
The article glosses over async Rust and is mostly a rant about how closures are difficult in Rust.

Most of the difficulty comes from Rust not having a GC yet wishing to keep track of object lifetimes precisely: a GC'ed language needs no distinction between an ordinary function pointer and a closure that captures the environment. But Rust being a low-level systems language chose not to have a GC.

Another popular language also doesn't have GC, makes distinctions between ordinary function pointers and closures, and its closures are unnameable types; that language is C++. But instead of using template for closures everywhere (like the STL), you can optionally use std::function<R(Arg)>, a type-erased closure type, which would in principle be similar to Box<dyn Fn(Arg) -> R> in Rust. But such a type doesn't really mention the lifetimes of whatever it captures, so in practice it doesn't work well in Rust (though std::function works well in C++ because the C++ doesn't care about lifetimes).

With that in mind, I see this article as not understanding the goals and tradeoffs of Rust. The author would be happier writing in a higher-level language than Rust.

skohan · 4 years ago
I think it's not entirely fair to paint problems with rust's async features as an aversion to low-level-ness.

If anything, I think the issue with Rust's async is that it tries to be too high level.

In my experience with async rust, most of the difficulty comes from "spooky errors at a distance". I.e, you're writing some code which feels completely normal, and then suddenly you are hit with a large, obtuse error message about types and traits you didn't even know you were working with. The reason being, something went wrong with all the inference involved in implicitly wrapping everything in futures. And often it's not even related to the line of code you just wrote.

So in these cases, I think the issue is that rust tries very hard to wrap async in a very smooth, sugared syntax. It works fine in a lot of cases, but due to the precise nature of Rust, there are a cases where it doesn't work. And because the system relies on a lot of magic inference, the programmer has to hold a lot of assumptions and rules in their mind to work effectively with async.

I am not an expert, but from my experience it feels like async rust was a bit rushed. It should have been introduced as a more explicit syntax first, with sugar on top once the guard rails had better been figured out through experimentation.

That is under the assumption that future-based async in rust is a good idea at all. Rust's ownership model becomes intuitive very quickly in the single-threaded case, but at times it feels like a square peg in a round hole where async is concerned.

gameswithgo · 4 years ago
This may be a problem with async generally unless the language is designed specifically around async like Go is, which some profound tradeoffs to make it happen. (not criticizing that I think Go did a great job)
Fiahil · 4 years ago
The article is also conflating synchronous single-threaded, synchronous multi-threaded and asynchronous programming.

Each have their own usage, and no, a multi-threaded program is not the same as an asynchronous one. For example, using threads and channels instead of async/await is not a design flaw if your workload is mostly about large, blocking computations on a read-only shared state with no I/O. In that situation, lifetime and closures will not pose any issue and won't get in the way because you're not mutating anything and communicate with owned messages.

The async ecosystem is evolving and will be better tomorrow than it is today. Saying that Rust programming as a whole is a mess because async/await is harder to use than necessary, is short-sighted. It's like saying you don't like apples, but you only chewed on the branch of the tree.

convolvatron · 4 years ago
its really strange that there are two languages running around together. one which is very opinionated in how to manage memory in a stack discipline and another which just uses reference counts.

they don't quite mix. so you need to be aware of which one you're (implicitly using), and you may need library functions for both colors.

you have to admit this adds some additional mental overhead. but what got me when trying to understand the language was just that there _was_ such a division. I actually tried to apply the lifetime model to asynch objects.

scotty79 · 4 years ago
From my very limited expeirience with Rust I noticed that it becomes way more easy and laid back language when you just skip using references and lifetimes nearly completely and just wrap everything in Rc<>. Then you are getting expeirience of fairly high level language with a lot of very cool constructs and features like exhaustive pattern matching and value types with a lot of auto-derived functionality.

Does Rc<> help this much with async as well?

a_rustacean · 4 years ago
`Rc` freezes the value as long as it's shared unless you use interior mutability. It's fine when you want to share immutable data, but if you ever need to mutate its contents you will have to deal with awkward cases and situations, and at runtime!

Wrapping everything in `Rc` is not a good default strategy. Instead figure out an architecture that works well with the borrow checker! This normally means thinking hard about your data structures and the ownership model of your application.

I love Rust precisely because of this. You can leverage the compiler to drive your development and design. If it's hard, then it's probably wrong!

spoiler · 4 years ago
You probably need Arc<> in the context of async, but if it's a normal closure, Rc<> will do.

However, that's not needed always. You largely wanna move stuff into closures in my experience, since the closure ends up owning the data.

Arc comes with a small performance overhead (Rc too, but less so I think?), so you don't want to use it all the time. When using Arc, you'll usually need a Mutex too, which adds its own cost, etc.

There are ways to partially avoid this costs, but they can add complexity (eg channels).

Sometimes you might be able to use an Async-flavor of the synchronization types instead, which come with more acceptable performance compromises in Async contexts.

mrfox321 · 4 years ago
If you are going to wrap everything in Rc, just use kotlin or c-sharp or python?
dgb23 · 4 years ago
Then you are essentially introducing a verbose, unoptimized garbage collector?
sweeneyrod · 4 years ago
You can get those cool features in languages such as OCaml/F#/Scala/Haskell, and then you don't have to worry about managing memory because you have a GC.
mgaunard · 4 years ago
Reference counting is mostly a cop-out for people who can't design code properly and reason about ownership and lifetimes.

It essentially makes your code Java and defeats the point of the language.

I would expect most decent teams to reject your code during code reviews.

jmull · 4 years ago
> I see this article as not understanding the goals and tradeoffs of Rust. The author would be happier writing in a higher-level language than Rust.

That's a pointless conclusion. The author's criticisms of Rust's tradeoffs are invalid because those are the tradeoffs Rust made. A perfect circle!

NoraCodes · 4 years ago
That would be circular, but that's not what the parent is saying. Rather, eta's post has one central point - she even bolds it for us:

> Rust is not a language where first-class functions are ergonomic.

And this... I mean, I don't agree with her, but that might be because I've been immersed in Rust for half a decade. But Rust's tradeoffs are based around four things. It is a:

- performant - reliable (incl. memory safe) - productive - systems programming language

"Systems programming language" means no garbage collector. "Reliable" means you can't just pass around references to memory willy-nilly. I'm sure closures and their associated traits (Fn(), "call it as many times as you want", FnMut(), "call it only when certain safety conditions are satisfied", and FnOnce(), "call it once") could have been fine-tuned more, but they _do_ achieve the goal of general, usable (imo) first class functions.

I don't see another design that would have done this.

pjmlp · 4 years ago
C++ lambdas are much better than Rust ones, because you can explicitly decide what to copy inside of the closure.

As for the author being happier writing in a higher level language, it kind of proves the point that Rust's main target is the domain where any sort of automatic memory management aren't a viable option.

Pushing Rust outside of this domain is only trying to fit a square peg into a round hole.

pitaj · 4 years ago
C++ lambdas need that feature because they don't have lifetimes or a borrow checker. Rust closures don't need that feature because rust has a borrow checker that ensures you aren't referencing something you shouldn't be.

You can explicitly decide what to copy inside a Rust closure as well, you just use a `move` closure and create references for anything you need referenced outside the closure instead.

Rusky · 4 years ago
> But such a type doesn't really mention the lifetimes of whatever it captures, so in practice it doesn't work well in Rust.

It can: Box<dyn Fn(Arg) -> R + 'a>

This is just the general syntax for any trait object with a lifetime.

ridiculous_fish · 4 years ago
Some of the pain inflicted by async Rust is incidental, not due to this GC choice. Pin is the biggest culprit.

Pin exists so that references captured in Futures may be implemented via raw pointers. This implies that a Future contains pointers to itself, hence Pin.

The cost of Pin is that it forces you to write unsafe code as a matter of course, the so-called pin-projections. [1] Look at the requirements for structural pinning: they are quite complicated.

References captured in Futures probably could have been implemented differently: base + offset, or relocations, or limit what can be captured, or always Box the state for a Future that captures a reference. Those would have avoided a GC and been even safer, since it wouldn't require unsafe code on the part of struct implementors.

1: https://doc.rust-lang.org/std/pin/#projections-and-structura...

pcwalton · 4 years ago
I don't think base + offset would have worked because then references would have to be codegen'd differently depending on whether they're in a future or not; this would have impacted separate compilation as you can pass references to other functions (including across FFI). Relocations only work at load time (either dynamic linking or link time), so they wouldn't work. Limiting what can be captured was tried with the old futures crate and it was a real nuisance. Boxed futures violate the zero-cost abstractions principle, which is the reason why many Rust users are using the language in the first place.

Pin is an evil, but it's the least evil of the possible options.

karmakaze · 4 years ago
I don't use Rust but was curious if this was a real or imagined problem--it seems to be the latter. Two points:

1. the example could have made a pure callback function that takes a mut& db param and pass thaf param to the 'do'er function. Why is this not required anyway? i.e. How does Rust know/decide who owns the mut& db if both the closure and the function that creates the closure can reference it?

2. as mentioned, the complaints about async seems to apply to closures in general even if used synchronously, AFAICT.

Best to think of this post as quirks when working with closures in Rust. It would have been far better to list each with workarounds.

brabel · 4 years ago
> the example could have made a pure callback function that takes a mut& db param and pass thaf param to the 'do'er function.

No. If you did that, you could only pass functions in that took the db as a closure argument, nothing else... you're thinking about this example only, and forgetting the problem is with the general case of capturing whatever is needed for the callback to work.

Matthias247 · 4 years ago
I agree that this one isn't about async Rust at all. The main comparison is drawn in

> And then fast forward a few years and you have an entire language ecosystem built on top of the idea of making these Future objects that actually have a load of closures inside

And this sentence is wrong. There's next to no `Future` implementations which are built on top of callbacks. And the reason why that's the way it is is exactly the one the author mentions: Callbacks don't work very well with Rusts ownership model.

Dead Comment

Aissen · 4 years ago
It was heavily discussed previously. In particular, it triggered this response from Rust contributor withoutboats: https://news.ycombinator.com/item?id=26410487

And this blog post from someone who did spend a lot of time working with async rust: https://tomaka.medium.com/a-look-back-at-asynchronous-rust-d...

IshKebab · 4 years ago
To be fair withoutboats didn't really address any of the points that people make about the flaws of async Rust (e.g. the cancellation problem).

It may be the case that there's no good way to solve them, but that doesn't mean that they aren't problems and it doesn't really help to say that people who highlight them are totally wrong and don't know what they're talking about.

That said I think your second link does a much better job of explaining the issues than this post does.

pornel · 4 years ago
withoutboats has explored these trade-offs on their blog, e.g. https://without.boats/blog/poll-drop/ and as part of io-uring investigation. io-uring is the main use-case for async drop, but even that has been mostly solved without language changes: there is tokio-uring now.

Apart from requiring a safe zero-copy io-uring abstraction to use a buffer pool instead of "bare" references, Rust's polling+cancellation model is fantastic. It's incredibly convenient to be able to externally time out and abort any Future, without needing to pass contexts/tokens and/or needing every I/O operation to have an explicit timeout option.

It's annoying when conscious small trade-offs are framed as fatal flaws (like the title of TFA). In the case of the cancellation model the "doesn't work" is that a safe abstraction is not entirely zero-cost. But when you use async in JS, C#, or Golang, you already have unavoidable heap allocations and memcpys (and another set of difficulties with cancellation). Rust's worst-case flaw is comparable to a normal mode of operation in a few other async languages. The scale of the issue is overblown, and its benefits are overlooked.

tptacek · 4 years ago
Is that provoked by the author of this post, or just a random commenter on the thread that post spawned? It looks like the latter.
hitekker · 4 years ago
I’d say both. The author of the post called Rust async/await a disaster, and the top commenter, who withoutboats replied to, agreed with the author’s criticism.
mirekrusin · 4 years ago
Wow, thanks, that was burried from me, thanks for surfacing it.
auggierose · 4 years ago
Oh, thanks for that link. Withoutboats is being quite emotional, but it is understandable to be emotional about their brain child.

I am tempted from time to time to try Rust, but in the end, I don't think it provides enough benefits for me to consider it. I like high-level, and when I am going low-level, I don't want to be hand-cuffed. It seems with Rust, I am paying all the time just for the ability to go into hand-cuffed low-level mode.

jokethrowaway · 4 years ago
I'd say seat belts more than handcuffs. When going low level it's easy to slip in memory leaks and security vulnerability.

If you care about developer speed and security but don't care about correctness or performance you can rely on someone's else implementation and write higher level code (eg. use node and offload low level security considerations to node).

If you care about developer speed and performance but don't care about correctness or security use a low level unbounded language (eg. write C and triple check your code)

If you care about security, correctness and performance but don't care about developer speed, use Rust.

To be frank I find the point about developer speed in Rust to be a bit exaggerated, it's pretty high level and the tooling is pretty great.

- Compiler speed is a bit hit or miss - Debugging life cycles definitely take some time, but with time you get the hang of it and you can quickly copy paste previous solutions to the current problem. - You save a significant amount of developer time by fixing things alerted by the compiler instead of finding errors at runtime

All things considered Rust is definitely the language I feel more productive in (before it used to be Haskell).

Oftentimes considerations about hiring / collaborating with other developers take the precedence over security, correctness and performance.

Right now I'm maintaining a node.js project, 2 Python projects and a Rust project - mainly because node.js and Python worked best with those teams and the Rust one is a solo project.

jdub · 4 years ago
Nah, you're really not.

Most of the time, particularly if you're not writing libraries, Rust feels like a dialect of Python that wants to help you get things right.

FpUser · 4 years ago
I am sort of in the same boat. I use C++ for server type applications. I consider modern C++ safe enough. While there are no explicit safety guarantees I am a practical man. My servers work for years without any complaints from clients. No memory leaks, no crashes. That's the end result and I think from that perspective for me using Rust would be ROI negative as I would have to port / replace a lot of code I reuse in my various products.

Things of course would be different should the client insist on implementation done in Rust but so far not a single client of mine ever mentioned using this language.

mgaunard · 4 years ago
The point of Rust is that your mediocre C++ developers won't be able to write some kinds of bad code patterns with it, since it won't compile.

The problem is that most likely those mediocre C++ developers won't be able to write any Rust at all.

If you do have good C++ developers, you're also probably better off sticking to well-established and battle-proven C++.

So the use case for Rust remains niche. It's a way to attract the programming language nerd that knows more about Haskell than he does about systems programming. Maybe not exactly the best fit? But worth trying if you want to differentiate yourself from the competition.

xondono · 4 years ago
Maybe it’s only my take, but from what I understand, the author wants to easily create closures with mutable references and call them from anywhere, asynchronously?

And the complaint is that Rust semantics makes this hard. Well yes, Rust makes it uncomfortable to shoot your own foot, that’s kind of the point.

microtonal · 4 years ago
Maybe it’s only my take, but from what I understand, the author wants to easily create closures with mutable references and call them from anywhere, asynchronously?

I think you are misunderstanding the author's point. I think that they'd probably agree that the (lack of) ergonomics of closures are a necessary result of the safeguards that Rust provides.

They point is more that, given that closures have somewhat frustrating ergonomics, it was probably a bad choice to base Rust's primary concurrency system on closures.

Disclaimer: this is not my opinion, just paraphrasing what I believe the author's point is. I found the post a bit strange, it seems to end rather abruptly. It doesn't seem to address the main question it seems to raise: what does this mean for async Rust and why is this bad?

pornel · 4 years ago
Rust has stopped depending on closures for async when it moved from experimental futures library to built-in async/await. Rust now easily supports mutable and temporary references in async blocks and across await points.

Rust still doesn't support use-after-free, so you can't reference a temporary object and use it in another thread that will outlive it :)

In TFA the second do_work_and_then() example doesn't compile, because it has a UAF vulnerability that borrow checker has detected and prevented.

uncomputation · 4 years ago
That was my impression too. Rust “makes it hard” for a reason: it’s not safe. If you work with the type system to prove that it is, you can go about and do your thing but Rust is about guarantees. That’s one great thing about it. You can’t ask it to just not guarantee something.
valenterry · 4 years ago
Why would the first example with the database be "shooting your own foot"? I'm not a Rust dev, but in other languages that code makes perfect sense to me.
kzrdude · 4 years ago
Rust has &mut references and & references.

The `&mut Database` sort of implies that the Database handle doesn't do its own locking or other needed state tracking - it just relies on the exclusivity of `&mut` in Rust to be sure that nobody else is modifying the database handle at the same time.

If the API used `&Database`, then it would have to be more flexible for simultaneous use of the handle. And then the closure would be (a bit) easier to call too, because it's not tracking that exclusive reference.

If the API used a reference counted `Database` handle, then it would release even more restrictions of lifetime management, etc. You can implement all of these in Rust.

In other languages, you usually end up with alternative two or three.

If you try to simultaneously take alternative 1 and use it in a way that's not supported, that would be shooting yourself in the foot. Except Rust says compile error instead.

uncomputation · 4 years ago
What if you do:

    my_fn(|| {
        db.store(42);
    });
    db.drop();
(Assuming you can just drop the database object, the semantics don’t really matter but the idea is the same.) The OP wants an easy way to do this but this is just not an easy thing to do. How can the closure guarantee that the database object will exist if my_fn decides to sleep for a couple seconds? Most languages do not have this problem because either: they use a garbage collector (Java and it’s cousins, Python, Go) or they are not safe (C, C++). Rust is in a particularly difficult (and ambitious and exciting!) intersection where this problem is tricky.

xondono · 4 years ago
For the same reason that keeping a shared excel file in a network disk is a bad idea.

The closure can write to database, and after the call, there’s no way to know if it has already written, or even if it’s writing to it right now.

api · 4 years ago
The author wants Go, Java, C#, or another GC’d language.
mplanchard · 4 years ago
I write async rust for my day job, and while it is more complicated to write than synchronous rust, the complications generally make sense once you understand how the compiler is managing the remarkably tricky task of async with no GC. Rust remains significantly more pleasurable to write than e.g. typescript for me.

Some patterns around error handling still a bit awkward, but the FuturesExt and TryFuturesExt traits help a lot there.

My only real “I have no clue what’s going on” moment so far has been with an error about traits not being general enough, but someone on the rust forums helped me out: https://users.rust-lang.org/t/trait-is-not-general-enough-fo...

Setting up clippy to disallow non-send-sync futures throughout the codebase has prevented that particular thing from recurring.

Noughmad · 4 years ago
The author spends the majority of the post saying how synchronous Rust doesn't work. The main problem? Closures have to be passed as traits. And they either don't know about the `f: impl Fn(i32)` syntax or refuse to use it for some reason.

Then the last paragraph just say "Oh and asynchronous Rust is even worse."

loeg · 4 years ago
In synchronous Rust, closures (largely) don’t need to have asynchronous lifetimes. You really don’t have to think about them all that much.
999900000999 · 4 years ago
Maybe I'm stupid.

But why is Rust so much harder than any other newish programs language.

Dart is like all of my dreams come true at once, Rust still gives me nightmares. I seriously tried to learn it multiple times and failed repeatedly.

I've created several Dart/ Flutter projects for myself and friends. Multiple C#/Unity projects. Python and JavaScript have paid my rent for the better part of a decade.

But Rust, I can't grasp basic concepts.

masklinn · 4 years ago
> But why is Rust so much harder than any other newish programs language.

All the languages you cited are essentially in the same space, they're runtime-heavy GC'd ("managed"), imperative, object-oriented, "applications" languages.

Rust tries to achieve memory-safety without runtime support (as well as low-overhead in general), and to do that it relies on a rather advanced type system with concepts which aren't formal in many commonly used languages (e.g. borrowing and affine types).

It's a different language, and if you approach it with the expectation that it's similar to other languages you know, it's probably not going to work well. Possibly unless the language in question is C++ (as Rust formalises a lot of best practices of C++), but even then the lack of object orientation and strictness of the compiler is going to make the transition difficult.

It's probably easier for people with less expectations that "all languages are similar" and that their knowledge will be easily transferrable, either because they have less experience period, or because they have experience with a much wider range of languages.

moonchrome · 4 years ago
> Dart is like all of my dreams come true at once, Rust still gives me nightmares.

Dart is the worst new language I've tried and it should have died back when they (in retrospective rightfully) abandoned DartVM in Chrome plans. (FWIW I used Dart back in the AngularDart betas, before Angular 2.0 was released, when TypeScript didn't even have support for async/await. Back then Dart had some good ideas, the tooling was good and it looked promising. In the meantime TypeScript did everything better while being backwards compatible and JavaScript improved a lot, along with the tooling. Now days Dart is strictly inferior in my view, the object model is closed/static, type system is nominal, so it has none of the scripting language qualities, and the runtime/metaprogramming is limited, with shitty library ecosystem - it's a shittier version of Java.

It's a language designed by VM developers and it shows in every way possible, so much emphasis placed on how the implementation works - for a high level language that isn't that performant in the end anyway and is hardly the bottleneck in it's usage scenarios.

Meta programming is done with compile time code generation and there is no runtime reflection, look at the libraries built for dealing with immutability for example - the ergonomics are Java level bad.

Flutter is a good idea if you need to write cross platform LOB apps (it becomes a bad idea if you need to use native components and render in coordination with them because the async channel native communication introduces visible render lag, eg. if you try to build custom rendering overlays over native maps it will lag frames behind because by the time you receive the map viewport updates and rerender the native map moved forward).

The fact that Flutter is built on top of Dart means I will not touch that framework any time soon, and would recommend anyone who doesn't like writing Java style boilerplate to avoid it as well.

They would need to include serious quality of life features to the language, and these features were requested years ago, but they move at a snails pace and prioritise other stuff.

999900000999 · 4 years ago
I'm going to have to disagree, straight up flutter fixes everything wrong with react native. I've wasted countless hours I'll never get back trying to get various Babel configurations to work. I guess typescript tries to fix issues with JavaScript not having real types, but you still have to fight Babel.

Why do I still need a bunch of weird configuration files to just get import working ? JavaScript has treated me very well, it's what allows me to pay my bills. It still has so many fundamental issues that I avoid it when I can.

The vast majority of apps ultimately either CRUD or LOB( thanks for the term). When I or a friend needs something hacked together real quick, Flutter is the answer.

Add in what might be the greatest Firebase integration, and that you can create CRUD apps in hours. Implement login, and authentication flows within minutes.

I don't feel like playing the true Scotsman game when it comes to getting things done, if it works it works.

In fact when I build these tools for my friends, or for myself I don't even build an app. I deploy directly to a website with Firebase and Flutter web. Google makes us workflow insanely easy, I could probably deploy a new CRUD app with a login system in like an hour.

Ultimately all we need to do is render a list of items, create new items, update them and delete them. That's what the vast majority of apps do.

At least for my personal projects it's Unity for games, and Flutter for anything else.

Then again, I don't have a comp science background and I just love getting things done. I don't really care how Flutter accomplishes what it does. Your allowed to use both Dynamics and Types when defining methods. This really helps when trying to hack something together fast, but latter reffing it.

If a friend needs an app built to track grocery spending or what not, what stack would you pick.

lordnacho · 4 years ago
I've found the opposite. Everything around the language is very ergonomic, people have written tools and libs for everything, nothing is hard to integrate.

The language itself seems ok too. If I didn't have the IDE tools to tell me what was wrong I'd be screwed, but once you run into your first few borrow checker issues you'll read up on what's actually going on and you can fix the problem.

Even the async stuff that the guy is writing about, I don't recognize. Normally async causes all sorts of issues, especially ones where you're totally stumped and the errors make no sense.

With Rust it's just been a breeze. Write some code and at some point the compile will heavily hint not to go that way. Especially with shared state type systems, a borrowing this way and that, you'll find that it's smarter to rethink the arch rather than add yet another Arc<RwLock<>>.

umanwizard · 4 years ago
Rust is IMO the _easiest_ language with no GC or runtime. It’s harder to produce working code in Rust than Python or Dart, but easier than C++.
kall · 4 years ago
All these other languages make things easier at the cost of performance and reliability. Maybe Rust has gotten to a point where it‘s hurt by it‘s own stellar reputation. It‘s so well liked that people pick it up for all kinds of projects. For people who know Rust very well, writing a mobile app or web service in it is probably fun and convenient, because it‘s always great to work with what we know best. For the rest of us, we should maybe stick with languages designed for application programming.
diegocg · 4 years ago
> But why is Rust so much harder than any other newish programs language.

For one, Rust has manual memory management. You are aided by the type system and the compiler, but it's the programmer who has to deal with the mental load of thinking about the lifetime aspects of every variable. Compare to a GCed language, where you just free your mind and can focus on your program.

kibwen · 4 years ago
To be more precise, I consider Rust to have "automatic static memory management", in contrast to C's "manual static memory management", or Java's "automatic dynamic memory management". The static part makes it harder than Java, because you do need to think about how to structure things, but the automatic part makes it easier than C, because the Rust compiler does the nitty-gritty for you.
yawn · 4 years ago
For me, when learning a new language, there's a war in my brain between learning the thing and being productive. If I'm not productive long enough, I jet. It took me a couple of tries of bouncing off of Rust before it started clicking. I've written a few personal web projects in Rust and I'm still on the fence using it for those. My comparable Go web apps run just as fast with a little more memory usage, but are significantly faster to iterate on (compiler speed) and write, and also to read/understand later. The thing that has been tripping me up the most as an intermediate Rust programmer is that some library authors tend to get Architect Astronaut-y with the type system. It gives me the SimpleBeanFactoryAwareAspectInstanceFactory Java vibes.
dndx · 4 years ago
Depends on the kind of programs you write, you may not actually need the "features" Rust provides (e.g. no-GC and high performance). In that case writing in a GC managed language certainly removes a lot of mental burden compared to writing in Rust.

However, anyone came from a systems programming background and wrote any non-trivial async network applications in C/C++ will most certainly appreciate the abstraction and safety Rust provides. Productivity grows significantly when writing in Rust because a lot of the low level details are handled by library authors instead of the programmer.

dgan · 4 years ago
Anecdotally, I found Rust easier to grasp than OCaml..
smabie · 4 years ago
I would say that Rust is a significantly more complex language than OCaml. As someone who knows both decently, I found Rust much harder to learn.

What problems did you encounter when learning OCaml?

vbg · 4 years ago
It worth the reward to keep trying.

You must be intentional about both coding and systemically learning at the same time how it works. Watch YouTube videos plus build a real world project.

My first attempt to learn rust failed. My second attempt was much better.

quotemstr · 4 years ago
Dart's type system is not sound.
mseepgood · 4 years ago
That's not true, at least not since Dart 2.
thibran · 4 years ago
> As someone who used to really love Rust, this makes me quite sad.

The current async story is still an MVP and I too dislike it. In the months before async, the ecosystem seemed on halt, waiting for async to land on stable Rust. Since then nothing has changed. The ecosystem "degraded" noticeable and has not recovered since. Maybe in future async will be great, but right now I try to avoid it.

... I still love Rust

pornel · 4 years ago
Sorry to spoil your axe-grinding with hard data, but the Rust async ecosystem has exploded since 2019. It is now about 5-6 times larger than it was before async/await landing:

https://lib.rs/crates/tokio/rev

And Rust as a whole keeps growing exponentially:

https://lib.rs/stats

thibran · 4 years ago
That's not what I was talking about. Before async we had one ecosystem. Now we have at last three: non-async, Tokio, async-std

My point is, the ecosystem experience has degraded since the async MVP is stabel.

darthrupert · 4 years ago
I think your comment hits the bullseye better than many others I've seen around this. It very much looks like the language stopped progressing with async.

Seems like they just tried to be everything at once, and reached some sort of a critical mass. It probably didn't help how Mozilla dropped lots of their Rust projects and staff at roughly the same time.

It's still a very good C++ replacement. I think its role as a higher level application language is more of an open question.