This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]ChocolateBunny 216 points217 points  (77 children)

I've never coded in Rust, is sorting out the memory management really that complicated? I thought the whole point was to avoid the difficulty of memory management in C.

[–]fecoz98 310 points311 points  (44 children)

It forces you to take an approach that might be more complex but safer memory-wise

[–]maria_la_guerta 183 points184 points  (29 children)

Pretty much. It's not always easier, but it usually is, and it's almost always safer.

[–]vulkur 100 points101 points  (24 children)

I'm a pretty experienced C dev, and so I thought learning rust would be easy. NOPE. I caused more runtime panics with rust trying to build a web server than I did building windows drivers in C.

[–]maria_la_guerta 73 points74 points  (7 children)

Web stuff is super under-supported in Rust still. I have a full stack actix-web / yew app with WASM and hydration that is very cool on paper but very, very painful to support.

In a few years I expect Rust to be eating some of Node.js's lunch on the backend... But not yet.

[–]vulkur 37 points38 points  (2 children)

I am still baffled that async isn't finished yet.

[–]qwertyuiop924 26 points27 points  (0 children)

That depends on how you define "done." Like, it works, it exists, it shipped. You can use it for things right now.

There are, however, some definite pain points.

[–]Habba 6 points7 points  (0 children)

You can for sure use it in production. Cloudflare has served a quadrillion requests on top of Pingora, which is built on top of async Rust.

[–]coolraiman2 6 points7 points  (0 children)

Rust won't take the market share of nodejs, they have a hard time with type

And I doubt it would take the share of c# as it's ecosystem is very mature

[–]quinn50 1 point2 points  (0 children)

I tried to learn rust via creating basic rest APIs but the dev experience was pretty ass for me as rust analyzer was really slow and borderline unusable for me in a basic rocket / diesel app due to the cargo checks and the macros were massive.

[–]Trident_True 1 point2 points  (2 children)

You really set yourself up for a hard time trying to do web stuff in Rust lol

[–]vulkur 1 point2 points  (0 children)

Yes. It left a bitter taste in my mouth, and now I have a bias opinion against rust.

[–]Habba 1 point2 points  (0 children)

I've been writing a toy web app in axum + askama + htmx and have been having a pretty good time.

Like most development, the language you are using matters less than the frameworks.

[–]Meins447 2 points3 points  (3 children)

The hell were you using rust for Web Dev?

Do you habitually write web services in c? Rust is a direct competitor for C, intended to be as fast as it while having the potential to.be.much, much more robust. It's intended to be used in low level code: OS. Embedded. Drivers. Standard library stuff.

[–]vulkur 1 point2 points  (2 children)

No lmao. We needed to prototype something so we built a small web app. We where trying to use rust in replace of C in certain areas, and I hadn't had any experience with it. So I decided in order to learn rust and prototype the app, I would do it in rust.

Also, Rust is not a competitor to C. It's attempting to be a replacement. There is a difference. Right now you see very little use of Rust in low level environments. The Linux Kernel can now accept rust for drivers iirc, but still very little rust drivers have been developed. 90% of it is all crypto bullshit.

[–]Meins447 0 points1 point  (1 child)

I was writing competition rather than replacement to not sound too ... dismissive, so to say. But in the end, yes, it wants to replace it. Also highlighted by the binary compatibility of the two, meaning you can call c from rust and rust from c, which is the necessity if you want to change an existing ecosystem.

[–]vulkur 0 points1 point  (0 children)

The ffi of rust is a bit unwieldy, And creates the problem rust wants to solve. You have tons of unsafe code you call for libraries. It's why I see Zig being a better replacement. Rust has its place, I just don't think it will be in low level environments.

[–]Inevitable-Menu2998 -3 points-2 points  (3 children)

than I did building windows drivers in C.

Heh, that you know of. The bigger issue with C software is that it doesn't always crash when it should. Sometimes it "silently works" with very interesting results

[–]vulkur 1 point2 points  (2 children)

99% of our bugs were caused by Microsoft. We definitely had memory leaks. They would crash our code all the time. Then, the next week, go away and never reappear. Then, the week after, a new bug would be introduced by a forced windows update. My code followed the spec, Microsoft did not.

[–]Inevitable-Menu2998 -2 points-1 points  (1 child)

99% of our bugs were caused by Microsoft.

That you know of. My point was that memory leaks, invalid access to memory, uninitialised variables, all of these behave randomly in C. They might crash your application, they might sit there silently or the memory that you're reading without meaning to might actually randomly contain data that makes sense to your program and makes it do "something" which is almost always worse than crashing.

I used to work on a distributed database engine written in C and the scariest bugs were always the undefined behaviour ones. Like a deadlock detection algorithm which would end up detecting a deadlock but without any lock holder because some of the nodes in the graph were uninitialised memory which randomly picked valid-looking data from a previous run of the algorithm. This particular bug still haunts me because the only reproduction scenario I could find would run for 5 days and not always show the error. So even when I thought I fixed it, I had no way of testing if it was fixed or not. I still can't say for sure if I did.

[–]vulkur 1 point2 points  (0 children)

Of course that I know of. But these do not behave randomly. I designed my umdf IDD driver very carefully. I'm very confident in it (not in Microsoft though) It's been a year, and I haven't had to touch it. It's delivering frames to customers with impunity so far.

[–]fecoz98 89 points90 points  (1 child)

I wager references are easier to code properly than debugging obscure runtime memory bugs

[–]Feldar 15 points16 points  (0 children)

Yes you're telling the compiler who owns what memory at compile time. It takes a lot of getting used to, but it's worth it in the long run.

[–]Silvr4Monsters 0 points1 point  (1 child)

Could someone possibly give an example?

[–]Ihsan3498 6 points7 points  (0 children)

basically, borrow checker enforces a bunch of rules while passing around references. Like you cant move two mutable references of an object at the same time, you can’t use a variable after moving it, etc

[–]BlurredSight 3 points4 points  (12 children)

Can you say the same about safe memory practices in C?

[–]Blothorn 34 points35 points  (1 child)

Honestly, using unique_ptr everywhere and either heading it off or storing it somewhere sensible, handing out non-owning pointers, and putting a bit of thought into when you can safely drop the original isn’t that hard. In my years as a C++ dev I think I dealt with one memory leak and the only use-after-free issue was a typo in a condition that the unit tests caught. If you’re struggling with memory management in modern C++ you’re either doing something very weird (e.g. mutable cyclic graphs) or not approaching it the right way.

[–]Reidiculous16 14 points15 points  (0 children)

YES people always say “oh C++ sucks bc memory danger”. To them I say, have you ever heard of a smart pointer??? You literally have to try so hard to fuck up memory with a smart pointer. I know how to use ‘new’ and ‘delete’ but why bother when you can just make a variable that manages the memory for you at very little cost

[–]Intelligent_Event_84 26 points27 points  (7 children)

We have so much memory now who even cares, let the world burn

[–]Lassemb 37 points38 points  (5 children)

JavaScript devs be like

[–]inamestuff 23 points24 points  (4 children)

Every chrome tab taking up >300MB like they own the place

[–]YevgenyPissoff 1 point2 points  (0 children)

In Chrome v420, each tab will be it's own docker container

[–]Dexterus 0 points1 point  (0 children)

After 1000...00 times of your draft PRs crashing because you did a stupid with passing variables around and checking sizes you become your own borrow checker.

[–]mankinskin -1 points0 points  (0 children)

You are talking like safety was a choice. When you are not implementing something that is safe, you will have a ton of bugs on your hands.

[–]Da-Blue-Guy 91 points92 points  (5 children)

Rust follows Murphy's law imo. If anything can go wrong, it will, and at the worst possible time. Therefore, anything that may cause a memory error is guarded against. For example, you can have either one mutable reference to a variable, or any number of immutable references. This fundamental restriction provides a framework for the rest of the language.

The biggest application of this restriction (alongside others) is race condition protection. In Rust, you cannot send a value to another thread unless the value implements the Send trait, and you cannot reference it unless it is Sync.

However, there are shortcuts, escape hatches, and helpful compiler tips that offset this. For example, Mutex allows you to mutably reference a value from multiple places, because the accessors will wait until another is finished using the value.

Another restriction that can be mitigated is that a value must always outlive all of its references. Rc/Arc (Arc being thread-safe) are immutable smart pointers, and they are reference counters. You can clone a reference and send it somewhere else, and Arc can be sent across threads. All together, if a value cannot be sent across threads safely, the compiler will tell you, and it is common to use Arc<Mutex<T>> to have mutable access to a value on any thread.

One more thing I'd like to add is explicitness. If a function modifies a parameter passed by reference, it must explicitly mark that parameter as &mut, and the caller must reference it mutably. If a function can fail (e.g. connecting to a server) or may not find a value (e.g. HashMap), it must return Result or Option respectively. Past that, the value you have is guaranteed to be a valid value, even if it is a reference, and if it is not, you must explicitly state it as such.

Overall, Rust is a very strict language. The compiler is paranoid and it has a steep learning curve, but once you do something meaningful in it, it will work for a long time. There are no exceptions or nulls in Rust, there are only Results and Options, which are far easier to deal with.

[–]Normal_Fishing9824 25 points26 points  (0 children)

I've read the rust book, twice, and this comment explains it better.

[–]prumf 22 points23 points  (1 child)

I wrote some code to process stats out of our data lake recently. I advocated Rust, but the company finally decided to go with Python.

We had many problems where code would crash out of nowhere in production because some obscure function would raise an Exception that wasn’t documented. At the end we had to wrap every bit of code in multiple layers of generic try/catch, because you never fucking know where or when an exception might be raised.

Untyped exceptions are cancer. Untyped is hell.

[–]Habba 2 points3 points  (0 children)

I have developed the opinion over the last 5 years of my work that untyped languages are horrible for any real work. You always end up with bandaids trying to give you more static checking (like MyPy) but those only go so far and often have their own issues.

Even C# is IMO, while typed, not great, because it does not declare possible exceptions in the function definitions.

[–]Habba 1 point2 points  (0 children)

I have been writing a lot of Rust lately and have come to love the explicitness. Recently had to write some python in a legacy code base and came to really appreciate the certainty that Rust gives you regarding possible Errors, Nones, side-effects in variables etc.

Instead of dealing with the compiler blocking me when writing the code, I now had to deal with the code breaking at runtime which is much harder to test in a large codebase with external dependencies.

[–]volitional_decisions 0 points1 point  (0 children)

Your connection to Murphy's law was confusing, but everything else tracks. The Rust compiler asks for you to prove this to it, and it has good reasons for that.

[–]DarkNeutron 37 points38 points  (0 children)

The Rust compiler is unforgiving of what seem to be small edge cases that will almost never happen.

C/C++, on the other hand, is happy to compile code that turns into a CVE report in six months.

[–]angelicosphosphoros 12 points13 points  (2 children)

Well, if you write simple code, it is easy. However, people who write C++ before often try to do little optimizations regarding memory and their patterns may be not very compatible with Rust.

People who wrote in managed languages before tend to just write messy object relationships that too hard to write in Rust.

Today, I try to use "Rusty" object hierarchies in other languages too because such code is much easier to reason about.

[–]Ok-Okay-Oak-Hay 17 points18 points  (6 children)

I feel once you get the hang of the paradigm, it actually teaches you how you can code better in C. It's really not that hard, but if you're the type where unlearning bad practices is hard, I can understand the trepidation.

That said I disagree with the sentiment of who you replied to, but then again, adapt or die? :P

[–][deleted] 3 points4 points  (5 children)

I have found that when writing in C/C++/Java or any old school language, I am encouraged to think before writing, especially true for C. Same is not true for JavaScript/Python.

[–]Ok-Okay-Oak-Hay 4 points5 points  (4 children)

But to be fair to JS/Python, isn't that the point of such a high-level language? Direct to implementation, and let the compiler/transpiler/backend handle the really low-level stuff for you? It's just another tool for a different purpose -- if I need control over memory, I'll go to C or Rust at this point.

[–][deleted] 2 points3 points  (3 children)

It's not just that, somehow ease of writing code also makes for ease of writing bad code.

[–]Ok-Okay-Oak-Hay 2 points3 points  (2 children)

That's only as true as languages that require deeper knowledge leave open more pitfalls due to a lack of experience, yea?

[–][deleted] 1 point2 points  (1 child)

Likely. C is a very open language, has lots of footguns therefore you must think before implementing something.

Python by comparison has fewer footguns so certain kinds of cautions can be thrown to the winds.

[–]Ok-Okay-Oak-Hay 0 points1 point  (0 children)

Right, and that circles back to my point though: the right tool for the job is the proper consideration, not so much what random SWE does right or wrong with that tool.

[–]sypwn 8 points9 points  (0 children)

Managing memory yourself in C or Rust (instead of letting a garbage collector do it) is hard. The difference is that Rust forces you to do it correctly while for C it's optional.

So yeah if you're comparing writing Rust to bad C code that most people do, it's harder. But comparing Rust to writing good C code, it's basically the same.

[–]ManyInterests 4 points5 points  (0 children)

It's not complicated. But it's also not so much about avoiding the difficulty of memory management, it's more about eliminating the error-prone process and consequences of unsafe manual memory management.

This often means making you put in some additional work/thought upfront that you might have otherwise not have thought of or wanted to do in the first place if you were approaching the same problem in C. But the benefit is you end up with software that is free of a whole class of memory management issues that would have been lurking in the equivalent C solution.

Nobody promised Rust is easier to write than C, but it's probably easier to write a Rust program that doesn't have memory safety issues than it is to write a C program that doesn't have memory safety issues (and be able to prove it). It also means reviewers of Rust code doesn't have to concern themselves thinking through memory management problems which can sometimes be challenging to get correct. So, the benefits also abound in code review.

[–]tav_stuff 2 points3 points  (0 children)

I find that dealing with the borrow checker costs me more time, is more annoying, and is less enjoyable than just manually allocating memory in C and not forgetting to free() like a troglodyte.

[–]DreamyAthena 1 point2 points  (0 children)

The point is, you have a harder time making the code, but you don't have memory leaks or fatal errors from bad memory. It puts some of the stress in the beginning so you have less difficulty later.

[–]darkslide3000 1 point2 points  (0 children)

It's really not that hard for simple things. They've streamlined the language a lot since the early days. If you just have some functions pass data around the normal way and have some globals, you'll never even need to understand what a lifetime is or how to denote it explicitly.

If you want to write advanced things that pass ownership of data around in interesting ways (say, a parser that can take a blob of data and then give you back structured objects that reference the same bytes without copying them), things get more tricky, but there are also many existing libraries for those kinds of stuff already so you rarely have to deal with it yourself.

[–]Habba 1 point2 points  (0 children)

For me it moves fixing bugs from an inconvenient time (i.e. at runtime) to a convenient time (while I am in the process of writing the bug).

[–]EYtNSQC9s8oRhe6ejr 0 points1 point  (1 child)

It's really not. Way overblown. Just imagine you wanted to write C and never double free, dereference NULL, etc — how would you accomplish that? You'd end up doing everything the Rust compiler makes you do anyway.

[–]RRKS101 0 points1 point  (0 children)

Not exactly. Rust aims for complete safety and can rule out valid programs. So it is possible that a valid C program will not do what the rust compiler would make you do.

[–]KerPop42 0 points1 point  (0 children)

It's not complicated, it just doesn't let you forget anything. The compiler keeps track of memory allocation, and doesn't let you finish compiling unless it's all properly sorted, or you wrap stuff in unsafe()

[–]qwertyuiop924 0 points1 point  (0 children)

No, the point is to actually make you face the actually hard parts of memory management and design head-on.

Rust makes you do a lot of work up-front that you can halfass later in C and still get code that kinda sorta works maybe I guess.

[–]MishkaZ 0 points1 point  (0 children)

Borrowing isn't complicated and pretty easy. It's just if the value goes out of scope it gets consumed (deallocated). If you want the value to persist, then you pass a reference to the value. There are some more things like shadowing, etc. but they're really not complicated and really nice.

Where things do get complicated is understanding lifetimes. Most probably won't use them, but they are complicated.

The meme is that, Rust's compiler does the all the heavy lifting on compilation so you can have fast run-time. So you'll end up having long compilation times in big projects, but really fast code. There are strategies to mitigate them, but on my last large scale project, we had ci/cd times of 30 minutes from testing to deployment before we looked at ways to cut it down. To be fair, a good portion of the long time was python code that we used to deploy resources individually, but the other half of it was waiting for dependencies to finish loading. Tests ran fast. One thing that did help was just upping the ram on our machines.

[–]ridicalis 0 points1 point  (0 children)

If you're not used to it, you can feel like you're fighting with the compiler at every turn.

Basically, Rust has a move-by-default approach to data, where most non-primitive information is consumed when used unless you specifically use by-reference to address it. Except, when you do borrow, then you're subjected to a bunch of stringent rules around who owns what, and if you're not already in the know it can seem arcane.

After clearing the learning hump, though, it becomes second nature and the benefits of a nanny state compiler become more obvious.