From Python to Rust: Some Key Differences and Takeaways | by Estelle Scifo | Feb, 2022

With side by side code snippets

Estelle Scifo
Photo by AltumCode on Unsplash

I have used Python for scripting, building websites with template rendering (hi Django!) and training machine learning models, while maintaining language consistency in a large code base, which is quite convenient. So when starting a new project from scratch, it was quite natural to also use Python.

On a general basis, it is always a good thing, at some point, to question your processes and choices. Always using the same language or tool, just because you are used to it, is not always the best decision making process.

So it is good practice to keep an eye on the tools other people are using, understanding how they work so that you can quickly identify when they are a good fit for you. And people talked a lot about Rust recently. It is even the most loved language according to the last Stack Overflow developers survey (2021):

The two languages ​​are fundamentally different. While Python lets you write a code without having to worry about your program memory allocation, thanks to a garbage collector program running in parallel with your code, Rust achieves memory safety through other mechanisms, its “ownership rules”. Understanding them is not the goal of this story, but you can find below an illustration of the different ways to access and “move” a variable (from one function to another for instance).

Source: https://rufflewind.com/2017-02-15/rust-move-copy-borrow

Privacy and immutability by default

In Python, everything (variables, classes, methods and attributes of classes…) is public and can be accessed from anywhere in your package. We rely on conventions to mimic privacy (like prefixing your variable name with an underscore; your IDE will raise of warning if you use that variable outside of its scope, but your code will run). On the contrary, in Rust, everything is by default private and you have to explicitly declare public objects with pub prefix, and export them to the parent module. It looks like a detail, but it has forced me to better think about public API for an application, which are the elements that I want other developers in my company to access in their own module, which are the ones that I want to expose to external developers and which are the ones that I consider should not be used outside of a very reduced scope?

Ownership

As a normal Python developer, ownership is something you have probably never heard about, but it is very powerful. The mind-blowing example of this concept which made me love Rust is the following. Consider a database transaction, from which you can execute queries, then commit or rollback and finally close the transaction.

Ownership example — Same code in Python and Rust

Add features with traits

I am sure you can understand the concepts and rules of ownership with a couple of examples. As I stated above, the most difficult part to me is doing without object oriented programming! I am used and I like the idea of ​​inheritance, allowing to encapsulate shared behaviors. This is not possible in Rust, or at least, Rust was not made with this target design. Instead, you would define a structure (containing attributes, the data) and traits (containing function definition). Then you can implement a trait for a given structure to describe how a given structure will behave under some actions.

Trait example — Same code in Python and Rust
Rust Trait inheritance and Fully Qualified Syntax for Disambiguation (see https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#fully-qualified-syntax-for-disambiguation-calling-methods-with -the-same-name)

Generics with constraints & dynamic dispatch

Third and last point I want to talk about today are generics. Since Rust is a strongly-typed language, it can become difficult to cover all cases. With the preceding example of a Cachable trait, we already have to deal with twice the almost same code, with only one difference: the type of the returned value from the serialize method. We can rewrite it using generics, by convention named T:

Rust Trait with Generics
Rust Generics with Constraints (here, T must implement the ‘Debug’ trait from the standard library)

I have been experimenting with Rust for a few months now. I think it provides a degree of safety and trust unreachable with Python. It has definitely broaden my mind in terms of code organization, beyond OOP.

Here are the resources I have used in my Rust learning path:

Leave a Comment