Learning to program in rust
-
The amount of people on the internet seriously complaining that both Rust error handling sucks and that
.unwrap();
is too verbose is just staggering.wrote on last edited by [email protected]I’ll be honest, when I was learning to program in Java I mostly just wrapped errors in an empty try catch to shut them up, with no regard for actually handling them.
I assume most other learners do that too.
-
I’ll be honest, when I was learning to program in Java I mostly just wrapped errors in an empty try catch to shut them up, with no regard for actually handling them.
I assume most other learners do that too.
Java requiring you to write every exception that can happen in your code isn't helpful.
Explicit error types are great, but Java managed to make them on a way where you get almost none of the upside and is so full of downsides that indoctrinated a generation into thinking knowing your errors is bad.
-
This post did not contain any content.wrote on last edited by [email protected]
The old school method of learning a programming language, database, framework or whatever was to read books and take classes, do a series of exercises that teach you how to use the features, and the errors you get if you don't do it right. Then you write code that way for like 10-15 years.
The Information Age method is to find some sample code, copypaste into an editor and hit Compile, then paste compile errors into google and fix them until there are no more. Then hit Run and copypaste/fix runtime errors until there are no more runtime errors. Old-schoolers used to call this hacking, but now it's called not having time to deeply learn the hot new thing because before you do you'll have to start over with the next hot new thing.
-
Call me a weirdo but the more errors a compilers give me the happier (albeit a bit frustrated) I am.
That stuff generally surfaces in a way or another… and I prefer at compile timeThat said I haven’t spent quality time with Rust yet… so not sure if there are a lot of nitpicks (ala go) or these are valgrind-level of “holy s*** I am so grateful to this tool”
The borrow checker makes things a bit more complicated to get running, definitely takes some getting used to when you come from a non-memory safe language. But the compiler is really helpful throughout almost all mistakes, often directly providing an explanation and a suggested fix. One of my favorites programming experiences so far
-
You mean mutex? Arc allows synchronous read only access by multiple threads, so it's not a performance bottleneck. Locking a mutex would be one.
Arc
is not free, and the extra atomic operations + heap allocations can become a bottleneck. -
The borrow checker makes things a bit more complicated to get running, definitely takes some getting used to when you come from a non-memory safe language. But the compiler is really helpful throughout almost all mistakes, often directly providing an explanation and a suggested fix. One of my favorites programming experiences so far
wrote on last edited by [email protected]...definitely takes some getting used to when you come from a non-memory safe language...
I actually think it's more like the opposite. The compiler takes the normal rules you apply to avoid issues with a non-memory safe language like C/C++ and enforces them explicitly where memory safe languages don't have those rules at all. I think lifetimes are much more confusing if you've never dealt with a user after free and usually let GC deal with it.
Also yes the compiler warnings and errors are amazing, the difference between rustc and gcc is night and day.
-
The old school method of learning a programming language, database, framework or whatever was to read books and take classes, do a series of exercises that teach you how to use the features, and the errors you get if you don't do it right. Then you write code that way for like 10-15 years.
The Information Age method is to find some sample code, copypaste into an editor and hit Compile, then paste compile errors into google and fix them until there are no more. Then hit Run and copypaste/fix runtime errors until there are no more runtime errors. Old-schoolers used to call this hacking, but now it's called not having time to deeply learn the hot new thing because before you do you'll have to start over with the next hot new thing.
Books, classes, and documentation can also be lacking for new tech.
-
The thing with OOP, particularly how it's used in GCed languages, is that it's all about handing references out to wherever and then dealing with the complexity of not knowing who has access to your fields via getters & setters, or by cloning memory whenever it's modified in asynchronous code.
Rust has quite the opposite mindset. It's all about tracking where references go. It pushes your code to be very tree-shaped, i.e. references typically¹ only exist between a function and the functions it calls underneath. This is what allows asynchronous code to be safe in Rust, and I would also argue that the tree shape makes code easier to understand, too.
But yeah, some of the patterns you might know from OOP will not work in Rust for that reason. You will likely need to get into a different mindset over time.
Also just in case: We are talking OOP in the sense of the paradigm, i.e. object-oriented.
Just using objects, i.e. data with associated functions/methods, that works completely normal in Rust.¹) If you genuinely need references that reach outside the tree shape, which is mostly going to be the case, if you work with multiple threads, then you can do so by wrapping your data structures in
Arc<Mutex<_>>
or similar. But yeah, when learning, you should try to solve your problems without these. Most programs don't need them.OOP also has object ownership hierarchy structures. Which object owns which other object, is a question always worth answering.
-
This post did not contain any content.
C is the way.3̶̧̧̳̉ẻ̵͙̗͍͒h̶͈̗̊͘o̷̡̳̥̒͐̇f̷͍̳͕̐{̸͇̀̒?̷̤͇̀̊p̴̰̆̍̕
-
OOP also has object ownership hierarchy structures. Which object owns which other object, is a question always worth answering.
Hmm, not sure, if I've heard of it. I'm guessing, we're not talking about simply drawing a UML class diagram...? Is it for figuring out which object will have to clean up which other objects, in non-GCed languages?
-
...definitely takes some getting used to when you come from a non-memory safe language...
I actually think it's more like the opposite. The compiler takes the normal rules you apply to avoid issues with a non-memory safe language like C/C++ and enforces them explicitly where memory safe languages don't have those rules at all. I think lifetimes are much more confusing if you've never dealt with a user after free and usually let GC deal with it.
Also yes the compiler warnings and errors are amazing, the difference between rustc and gcc is night and day.
wrote on last edited by [email protected]I can confirm, I've never used a non memory managed language, and the Rust borrow checker is a massive kick in the teeth
But, the more i consider it from the perspective of memory, and pointers, the borrow checker makes a lot of sense
Especially when storing references inside structs, and how mutability affects references
I actually figured out i could fix a re-mutable borrow error by performing the two mutable operations in separate for loops
-
Arc
is not free, and the extra atomic operations + heap allocations can become a bottleneck.Oh, I did not know that. Well, it makes sense that it has a heap allocation, as it becomes more or less global. Though not sure why the atomic operations are needed when the value inside is immutable.
-
Oh, I did not know that. Well, it makes sense that it has a heap allocation, as it becomes more or less global. Though not sure why the atomic operations are needed when the value inside is immutable.
How can you otherwise keep track of an object's lifetime if copies are made concurrently?
-
Hmm, not sure, if I've heard of it. I'm guessing, we're not talking about simply drawing a UML class diagram...? Is it for figuring out which object will have to clean up which other objects, in non-GCed languages?
wrote on last edited by [email protected]Yes, pretty much like UML diagrams. Who is responsible for allocating memory and freeing it.
Languages like Swift, Objective-C, C++ have features that mean you don’t need to do this by hand. But you have to tell the compiler if you want to keep and object around and who owns it.
See this article on Objective-C to see the different ways to manage memory this language supports.
-
The old school method of learning a programming language, database, framework or whatever was to read books and take classes, do a series of exercises that teach you how to use the features, and the errors you get if you don't do it right. Then you write code that way for like 10-15 years.
The Information Age method is to find some sample code, copypaste into an editor and hit Compile, then paste compile errors into google and fix them until there are no more. Then hit Run and copypaste/fix runtime errors until there are no more runtime errors. Old-schoolers used to call this hacking, but now it's called not having time to deeply learn the hot new thing because before you do you'll have to start over with the next hot new thing.
The last language I learned was Rust, I did a mix of the two. I read through the canonical Rust book and then got to coding because I learn more deeply when I can apply what I've learned. It's still a tricky language to keep a conceptual model of in your head though.
-
Yes, pretty much like UML diagrams. Who is responsible for allocating memory and freeing it.
Languages like Swift, Objective-C, C++ have features that mean you don’t need to do this by hand. But you have to tell the compiler if you want to keep and object around and who owns it.
See this article on Objective-C to see the different ways to manage memory this language supports.
Ah, interesting. I went from garbage-collected languages where thinking about ownership might be useful for keeping complexity low and occasionally comes up when you manage lists of objects, but ultimately isn't needed, to Rust where well-defined ownership is enforced by the language.
So, I wasn't aware that ownership is even as concrete of a thing in other languages...
-
Ah, interesting. I went from garbage-collected languages where thinking about ownership might be useful for keeping complexity low and occasionally comes up when you manage lists of objects, but ultimately isn't needed, to Rust where well-defined ownership is enforced by the language.
So, I wasn't aware that ownership is even as concrete of a thing in other languages...
Oh you need this in garbage collected languages too, once you run into memory use issues. GC languages are notorious for being wasteful with memory, even when working correctly.
-
Oh you need this in garbage collected languages too, once you run into memory use issues. GC languages are notorious for being wasteful with memory, even when working correctly.
Not if you never get your application into production...
I wish this was as much of a joke as I'm pretending. It's so common for software projects to get cancelled that lots of tooling differences are just in terms of how long they let you not deal with long-term problems and how violently they do then explode into your face.
For most of the development lifecycle of a GCed project, you're gonna ignore memory usage. And if you're lucky, it can be 'solved' by just plonking down a thiccer piece of hardware...
-
Not if you never get your application into production...
I wish this was as much of a joke as I'm pretending. It's so common for software projects to get cancelled that lots of tooling differences are just in terms of how long they let you not deal with long-term problems and how violently they do then explode into your face.
For most of the development lifecycle of a GCed project, you're gonna ignore memory usage. And if you're lucky, it can be 'solved' by just plonking down a thiccer piece of hardware...
Great points. Especially putting in more memory can get you very far.
Database optimization? Nah, just put in 1 TB of RAM to keep the whole DB in memory at all times.
-
The amount of people on the internet seriously complaining that both Rust error handling sucks and that
.unwrap();
is too verbose is just staggering.I will say this: for me, learning rust was 80% un-learning habits from other languages.
People tend to not like it when they have to change habits, especially if those took a long (and painful) time to acquire.
In this particular case, this is the same complaint Go faced with its form of explicit error handling. And Java, for that matter.
Honestly, Rust does a killer job of avoiding checked exceptions and verbose error hooks by way of the
?
operator, and requiring all possiblematch
branches to be accounted for. If you embrace errors fully, by usingResult<>
and custom Error types, your program gets a massive boost in robustness for not a lot of fuss. I recently learned that it gets even better if you embraceenum
as a way to define error values, and make sure it implements useful traits likeFrom
andDisplay
. With that, error handling code gets a lot more succinct, permitting one to more easily sift through different error values after a call (should you need to). All of that capability far exceeds any perception of clunkyness, IMO.