14 ms·
This article demonstrates well something I've seen a lot with people coming from GC languages getting into Rust: they just write the code the way they're used t
by simias 5y ago
This article demonstrates well something I've seen a lot with people coming from GC languages getting into Rust: they just write the code the way they're used to and work around the borrow checker by slapping `Arc<Mutex<_>>` all over the place.
Then that leads to frustration and wondering why you even got rid of the GC in the first place if you end up with a crappier non-transparent reference-counted garbage collection with all these Arc/Rc. Some devs even seem to think that non-GC devs are silly poseurs who refuse to have a garbage collector only to reimplement it manually with reference counting. We are silly poseurs alright, but we're more efficient than that!
I share the author's conclusions: don't do that. If you find yourself slapping Mutexes and Arc/Rc all over the place it probably means that there's something messed up with the way you modeled data ownership within your program.
- cogman10 5y agoYup, definitely a frustration I have reading through rust tutorials as well. So many of them will get out of memory problems by "Oh, just add Arc around it".
- PragmaticPulp 5y agoThis is also a problem with Rust conversations and tutorials online: There's a lot of talk about what not to do or what you can't do in Rust, but it's rarely followed up with an explanation of what users should do. Mutexes aren't an entirely foreign concept to many programmers. Obviously if someone can architect their system in a way that doesn't require Arc<Mutex<_>> then go for it, but we need to be careful about giving blanket advice without alternatives.
- rmdashrfstar 5y agoIndeed, this blanket advice does not apply in situations where shared resources that require mutable access need to be synchronized. If my async HTTP router is serving two requests that are using the same database client to persist changes, that needs to be synchronized. How does one do this without a mutex?
- ibraheemdev 5y agoUse a database pool, although some pool implementations may use mutexes internally.
- barsonme 5y agoBy acquiring and releasing resources. I’m not intimately familiar with Rust, so I’ll use Go as an example. Create a one-buffered channel: `make(chan T, 1)`. To acquire, receive from the channel. If the object is in use, the receive will block (goroutine put to sleep) until it’s available. To release, send the acquired object to the channel. On a language level, no mutexes.
- waffletower 5y agoThere are STM (Software Transactional Memory) implementations in Rust.
- lmm 5y agoThe lifetime/borrow-checking is the mutex. If you've got a database client that must only be used by one caller at a time, make a function that takes the client as &mut.
- bjornjajayaja 5y agoI think the broader problem is that: one _cannot_ just arbitrarily “go” from a higher level language to a systems language. Folks really ought to read the C/C++ literature to understand why Rust evolved in a unique direction. That gives better compare/contrast. Also, a lot of Python idioms are actually from C++ (e.g. mixins, iterators, operator overloads, etc). Anyway, to reiterate: folks interested in systems languages should read the C/C++ literature and actually become a systems engineer first.
- whatshisface 5y ago>Folks really ought to read the C/C++ literature to understand why Rust evolved in a unique direction. That gives better compare/contrast. Sounds like a good idea for the post-singularity age of infinite lifespans, but what folks really need to do is learn what they need to learn as best they can when they need it.
- bjornjajayaja 5y agoMaybe if one just wants to scrape by. I wouldn’t want systems code from those folks to be honest... Maybe that’s fine up the layers but the whole point of Rust is correctness and stability—to get the that point, maybe invest a month hitting the books?
- heresie-dabord 5y agoWhenever I see the word "pythonic" I am cautious. Not all languages are Python, nor should they all become Python, nor are they worse for _not being Python_.
- radicalbyte 5y agoPython developers try to write idiomatic Python in whichever language they use. Learning the idioms and conforming to a new language are hard, especially at the beginning, and people are lazy.
- lmm 5y agoPython has a reputation as "executable pseudocode", a language that makes it easy to express the essence of what you're actually doing without clutter or ceremony. That's a worthy design goal for every language IMO.
- throwaway894345 5y agoIn fairness, "just use Rc<>/Arc<>/clone()/etc" is common advice from the Rust community in response to criticism that the borrow checker puts an undue burden on code paths which aren't performance sensitive. > If you find yourself slapping Mutexes and Arc/Rc all over the place it probably means that there's something messed up with the way you modeled data ownership within your program. It only means that the data model doesn't agree with Rust's rules for modeling data (which exist to ensure memory safety in the absence of a GC). This doesn't mean that the programs the user wants to express are invalid. And this really matters because very often it doesn't make economic sense to appease the borrow checker--there are a lot of code paths for which the overhead of a GC is just fine but lots of time spent battling the borrow checker is not fine, and I think Rust could use a better story for "gracefully degrading" here. I say this as a Rust enthusiast. EDIT: I can also appreciate that Rust is waiting some years to figure out far it can get on its ownership system alone as opposed to building some poorly-conceived escape hatch that people slap on everything.
- fpgaminer 5y ago> very often it doesn't make economic sense to appease the borrow checker--there are a lot of code paths for which the overhead of a GC is just fine but lots of time spent battling the borrow checker is not fine To me it's not about performance. A little bit of time spent now appeasing the borrow checker will pay off ten fold later when you don't have to deal with exploding memory usage and GC stalls in production. GC is great for quick hack jobs, scripts, or niches like machine learning, but I believe at this point it's a failed experiment for anything else.
- throwaway894345 5y ago> To me it's not about performance. A little bit of time spent now appeasing the borrow checker will pay off ten fold later when you don't have to deal with exploding memory usage and GC stalls in production. I'm confused by the "it's not about performance. [reasons why it is, in fact, about performance]" phrasing, but in general a lot of applications aren't bottlenecked by memory and a GC works just fine. Even when that's not entirely the case, they often only have one or two critical paths that are bottlenecked on memory, and those paths can be optimized to reduce allocations. > GC is great for quick hack jobs, scripts, or niches like machine learning, but I believe at this point it's a failed experiment for anything else. That sounds kind of crazy considering how much of the world runs on GC (certainly much more than the other way around). I feel the need to reiterate that I'm not a GC purist by any means--I've done a fair amount of C and C++ including some embedded real time. But the idea that GC is a failed experiment is utterly unsupported.
- Dzugaru 5y agoI come from C# and Rust absolutely drives me bonkers. I try to reference all the things all the time, cause its ingrained in my brain as more efficient than copying stuff around or/and requesting things from hashmaps. The first thing I've tried to implement was a string interning module and I was stuck on it for three days (I still don't know how to do it without duplicating each string twice). Rust is completely orthogonal to the concept of a nice garbage collected graph of objects.
- nicoburns 5y agoMaybe check out one of the existing string interning modules? There are some good ones. I believe it's common to internally use indexes instead of references to get around the borrow checker in these kind of situations.
- Dzugaru 5y agoThat's my impression with Rust for now - you use indexes instead and borrow as needed. Long lived borrows are a no go, I was shocked to figure out you cannot return a new thing and a reference to it from a function.
- deleted 5y ago[deleted]
- CraigJPerry 5y agoI’ve never tried that so i don’t know the challenge but my first thought was that it doesn’t seem incompatible with rust’s ownership model - assuming an interned string has static lifetime and then hand out immutable references?
- Dzugaru 5y agoI wanted to drop strings which are not used anymore, you do need Rc for that (no way around it I think), but my problem was that I also needed that strings as a key to HashMap<String, Weak<String>>. So I use that HashMap to check if the string is interned and give a new counted reference as needed and also periodically check any weak refs that should be dropped. This works, but the key better be immutable (I think?) so you cant do HashMap<Weak<String>, Weak<String>> (HashSet?) and hence need second copy as a key.
- ragnese 5y agoI'm ambivalent about it. In my opinion, Rust is optimized to be a systems programming language. As such, we'd expect that it shouldn't really be your first choice for writing an "application". (I'm not referring to the OP at all here) HOWEVER, Rust is such an excellent language that we all want to use it to write applications ANYWAY. That's kind of amazing in and of itself- that people want to deal with a non-GC'd language at all to write applications. Because, really, garbage collection is awesome and there's almost zero reason to avoid it unless you need extremely predictable performance, or very low runtime overhead, etc. As far as wrapping everything in Arcs and Mutexes is concerned: Yes, that's ugly and it's a lot of extra typing. On the other hand, your performance is still likely to be orders of magnitude faster than Python for some general application-type tasks, and it will likely avoid headaches with the borrow checker, etc. So, honestly, I don't know if I recommend doing that or not. Want I want is to be able to tell people to not use Rust for what they're doing. I'd like to have a different recommendation for a "garbage collected Rust" but there really isn't anything that I think is good enough for the title. Maybe Scala 3 (I haven't played with it yet) or OCaml when its multi-core stuff is done. Maybe F# or C# are good enough, too.
- Zababa 5y agoThe problem with OCaml is not multicore. If it had an easier to use build system and a larger community (so more libraries), it could be great for server-side software. But dune is way harder to use than Cargo and you can often miss libraries. Here's an example of Dark moving away from OCaml to F# https://blog.darklang.com/leaving-ocaml/ https://blog.darklang.com/leaving-ocaml/, which doesn't have as much this problem because you can use C# stuff. Scala also doesn't suffer as much from it, part of it because it was really popular at some point, and part of it because you can use Java anyways. I hope at some point we'll have this "garbage collected Rust", but for now OCaml, Scala and F# all have a worse developer experience than Rust. I could add Haskell to that too. I've been thinking for some time that something like Go but based on a ML would fill this "garbage collected Rust" niche quite well. Maybe something built over Rust itself to leverage all the ecosystem? You could write all of the glue code in this language, have access to a large ecosystem of libraries and have an option for high performance code. This would also complement Rust nicely: I know that OCaml has a really fast compiler, which would be a breath of fresh air for the community.
- axegon_ 5y agoCouldn't have summed it up better if I tried. Rust is an awesome language but at the end of the day it's a systems language. As such it's not particularly suitable for writing just any type of application. Seems it shares one of my many problems with javascript: The fact that everything is being written and re-written in javascript(even if there are different reasons for both). As I've said a million times, "just because you can, doesn't mean you should". The endless Arc<Mutex<_>> is a symptom of it. In larger application this turns into an endless hell of read/write locks and sooner rather than later it blows up straight into your face. Which is further amplified by the somewhat recently introduced async/await. And let's not overlook the many incompatible tokio versions and the fact that tons and tons of crates use different versions of tokio: Just as everything looks buttery smooth, BOOM: thread 'main' panicked at 'not currently running on the Tokio runtime.' Damn it... Awesome... Which one is it this time... On the subject of python, I think Rust has a very powerful niche it could take: slightly modifying an old Google philosophy(which they can't stick to anymore for a million and one reasons of course): "Python where we can, Rust where we must". To my mind it could be a very pleasant recipe for a large number of startups.
- hazz99 5y agoI’m new to Rust, and definitely guilty of all this :) do you know of any good resources which discuss transforming your data model to align with the borrow checker? Or is this just something that comes naturally with time?
- steveklabnik 5y agoMore of the latter right now, but things like “data oriented design” can help nudge your thinking in the right direction. I would love to see more writing on this.
- zozbot234 5y ago> I share the author's conclusions: don't do that. If you find yourself slapping Mutexes and Arc/Rc all over the place it probably means that there's something messed up with the way you modeled data ownership within your program. That's just not a very useful statement to a beginner. Rc/RefCell and Arc/Mutex have their use, for data with patterns of lifetime or mutability that cannot be derived at compile time based on program syntax - something that comes up all the time in practical programs. Of course one should seek to refactor these things out whenever possible (among other things, programs that use shared data heavily are also harder to survey wrt. correctness), but to say as a generalized statement that there's no case for this feature is just not correct. The feature is also intentionally heavy on syntax so there's no missing where you're using it.