Don't write Rust like it's Java (2023)
jgayfer.com
jgayfer.com
But I've mostly used/debugged groovy code so maybe I'm partial.
That's my opinion, anyway.
It was C++, if we are being historically accurate. It’s actually a positive in Java’s case that it managed to make these systems even run.
Including those stupid "coding Java in C++" remarks, when Java OOP model is based on a mix of C++ frameworks, and Objective-C features.
If you try to program Rust like C you are going to fight and lose.
If you try to program Rudt like and Object Oriented Language you will eventually go mad.
If you like a Zen master let all previous knowledge and pre-conceived notions of what beautiful code has to be out of the window and do it the Rust way, suddenly things flow and stop being a pain.
And that experience will help you be a better programmer, period.
This is not at all the reason. The real reason is that Rust chooses to make the runtime overhead required for dynamic typing and heap allocation explicit, not anything to do with memory safety.
Java puts everything into the heap and creates easy expressability by having pointers everywhere. In contrast, Rust often avoids pointers, prefers the stack and is very stingy with memory access.
These languages are completely orthogonal to each other and it has consequences in their usage. Not that a good chunk of people wouldn’t butcher the code they are given in any codebase…
When Valhala is finally merged, then value class and value record will also not be on the heap by default.
Finally, even without Valhala, Unsafe and Panama allow for off heap allocation, thus again, not everything.
What makes Java so good is that it is a language written for creating high quality libraries and frameworks. Sure, it will be nice to define your own zero-cost classes but the largest benefit will stem from improved an standard library as well as an enabler for high-performance programming and GPU access.
I’m not sure if I would recommend it if you’re working as part of a team though, other java devs will hate your code lol.
Unfortunately, most of them still suffer from premature and very unnecessary abstraction bloat. Luckily, the trend for terseness and minimalism is gaining more and more momentum each year, with just a single 100 LOC Program.cs microservices with asp.net core's minimal API no longer raising eyebrows and causing arguments in the teams as much as it used to. Extensive support for pattern matching, records, closures (since a long time ago) and other ways to define strict and terse data structures is also helping to preserve functional nature of this approach.
Note that JVM does not have structs and has a long way to go until project Valhalla becomes a reality. FWIW it has been less of an issue thanks to quite strong escape analysis as implemented by OpenJDK and GraalVM, but it does not afford the same guarantees and degree of control the structs do (not to mention lack of monomorphized struct generics as in Rust and .NET). In the "average codebase", structs in .NET have not been as popular historically, but with heavy focus on "close to the metal" features in both compiler optimizations and syntax, and increasing awareness of C# being a viable and effective systems programming language, this also is changing.
Most developers likely don't stick around long enough to see the pains of doing major upgrades on large codebases.
I've been using a layered architecture with great success for a large fintech application. We've even migrated from one web framework to a completely dissimilar web framework trivially as the majority of our code in the business layer needed no changes.
An enum often suffices to express the different combinations.
Of course, from a pattern perspective, that is "not extensible" but it often ends up being easier to maintain and in Rust, often compiles down to more efficient code.
Don't write [insert lang here] like it's Java.
I've seen this in Ruby code bases, TS code bases, even Erlang code bases (using gen servers like an OO primative.)
Maybe there's a certain safety in doing thing how they've always been done - even if that seems backwards to me.
Of course what's being picked upon is a specific kind of Java code. It's better to be specific than technically incorrect, as all written Java is Java regardless of how you write it. The article goes a good job (comments here less so), except for this bit:
> While not entirely accurate, there’s some truth to the trope that Java developers need everything to be an interface (I am one such developer).
That's not just Java, that's Dogma.
What does work for me is writing in most languages (Java included) as if they're functional ones without lots of variable reassignment or data mutation.
Java makes this king of separation between data and logic really hard.
Kotlin came to the rescue for us. It allows us to move our Java/OO codebase in a slightly more FP direction: uncouple data and logic, immutability is preferred, no (or very little: yes looking a you shitty "platform types" in Kotlin) implicit nulls.
Discussing curriculum and textbooks for a Java class today, someone recommended a book that was most recently updated for Java 7. When I pointed that out, the response was:
“The level we teach at, the new Java features won’t make much of a difference.”
Java is a culture just as much as a language, and the culture is stuck somewhere in the past in a lot of places, especially in education.
I suspect the author might be reading this post back and cringe a bit. That last code listing with handle_session_completed triggers my inner clippy. Why clone session.customer_id to then pass it as a reference? Why are those CheckoutSession fields Options?
The other advice I would give is to identify what the author calls "Service" as what is called "view type" or "newtypes" in rust. A newtype wraps another type, to give it a different set of methods. For example, a counter could wrap an i32 and only allow incrementing and reading the value. Or put together a set of references to fields/slices of a struct.
It can be useful, but the way it's used by the author here is not very rusty, and the final suggestion of using a function is a descent alternative. Although I think defining handle_session_completed as a method on CheckoutSession or UserRepo might be better.