August 9th, 2024

Don't write Rust like it's Java

The author discusses transitioning from Java to Rust, highlighting Rust's type safety, differences in traits and interfaces, complexities of ownership, and the importance of embracing Rust's unique features for effective programming.

Read original articleLink Icon
Don't write Rust like it's Java

The author reflects on their experience transitioning from Java to Rust, emphasizing the importance of adapting to Rust's unique features rather than trying to force it into a Java-like structure. They appreciate Rust's type and memory safety, which can catch many errors at compile time that would otherwise occur in dynamic languages like Python. However, they caution against treating Rust as a direct replacement for Java, particularly in terms of object-oriented programming. The author discusses the differences between Java interfaces and Rust traits, noting that while traits serve a similar purpose, they require different handling, such as boxing or using generics. They also highlight the complexities of ownership and lifetimes in Rust, suggesting that sometimes using functions instead of service objects can simplify code. Ultimately, the author encourages embracing Rust's idioms and paradigms to fully leverage its capabilities, rather than imposing familiar patterns from other languages.

- Rust offers strong type and memory safety, catching many errors at compile time.

- Rust's traits differ from Java's interfaces, requiring different handling techniques.

- Ownership and lifetimes in Rust can complicate service dependencies.

- Using functions instead of service objects can simplify Rust code.

- Embracing Rust's unique features is essential for effective programming in the language.

Link Icon 15 comments
By @carlmr - 4 months
Of course this is on point, you shouldn't write Rust like it's Java, however looking at Java, you shouldn't write anything like it's Java. Java has brought forth the biggest madness of tacking on OOP to everything that is still making 3 billion software projects unreadable.
By @atoav - 3 months
Generally good advice is: Don't program Rust like nearly any other programming languages you know.

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.

By @niederman - 3 months
> Remember that great feature of Rust being memory safe? It comes at the cost of not being able to easily “inject” something that implements a trait.

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.

By @DarkNova6 - 3 months
Everybody worth their salt should know that those languages have completely different working models in mind.

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…

By @left-struck - 3 months
I’ve also had some success writing Java more like as if it’s rust, using Java Records instead of classes to create immutable types.

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.

By @wh33zle - 3 months
I find myself defining my own traits very rarely these days. Traits enable polymorphic actions on data. Most of the time, you simply don't need that.

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.

By @weatherlight - 3 months
I feel like this axiom is true in general.

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.)

By @Sytten - 3 months
Sometimes it is easier to arc dyn your way out of things especially in the async world and if you need to decouple two parts of the software. You get a sort of slower ref count gc similar to swift but it makes your life more enjoyable, always depends on your usecase.
By @cies - 3 months
I look at Rust more as an FP lang than an OO lang. By FP I mean: you have data and logic; never a combination of both (classes/objects).

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.

By @sshine - 3 months
Don’t even write Java like it’s Java!

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.

By @binary132 - 3 months
I think ownership and especially lifetimes are a lot harder to reason about than the trait system tbh, even traits implementing traits, GATs, impl trait, and so on and so forth.
By @psadauskas - 3 months
While we're at it, don't write Ruby like its Java, either (Not to name names, but I'm looking at you, OpenTelemetry gems).
By @nicopappl - 3 months
Yoo, congratulations to the author for going from this blog post dated to October 2023 to shipping an actual rust crate (bevy_light_2d)!

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.