Some Rust peculiarities

After first investigating Rust superficially some time ago, I’ve now started to really use it. As I’ve written before, my programming background is C/C++, Fortran, C#, and more recently Python. With that experience, some things about the Rust programming language strike me as peculiar.

No return

Rust does not require use of the return statement in a function that returns a value. Simply write the variables without return and ;. This is one of the many things that cargo clippy identifies. Clippy can be quite useful for transitioning to writing idiomatic Rust code. It e.g. also indicates where you can use iterators instead of indexing in for loops, which might even speed things up.

return does of course still have its use – when you want to prematurely leave a function.

Ownership

In order to be able to create memory-safe programs without employing garbage collection, Rust tracks ownership of objects. How this works depends on whether a variable is stored on the stack or the heap. This is something that you don’t need to worry about in Python, but in Rust it matters. Variables on the heap (which is everything where the size is not known at compile time) get moved into functions, which then takes ownership – unless you make an explicit copy. The Rust Book has a good explanation of how ownership works.

References

The solution to avoiding copies of data on the heap is the use of references. The distinction between values and references will be familiar to C++ programmers, but in Python, everything is a reference – which means that the distinction is hidden and thus not obvious to the programmer. This may cause unexpected behavior. In Rust, is always  obvious whether you are dealing with a reference or not – and, of course, whether it is mutable or not. References in Rust are explained here.

No inheritance

Rust does not allow for true object oriented programming in the strict sense as it is known e.g. from C++ and Java. There are no true classes. Most of their functionality is provided by structs and the ability to define methods for them. You can of course use structs as attributes of other structs, so some form of inheritance can be achieved in this way. Method syntax uses the self keyword that you might be familiar with from Python. As a long-time C++ programmer, it still hurts my eyes and annoys me, but I’m getting used to it.

No polymorphism

In Rust, it’s not possible to define two functions with the same name but different parameters. I understand it from a design point of view. It’s easy to make the assumption that two functions do the same thing because they have the same name, and run into issues when they don’t. Some degree of polymorphism can be achieved with generic types (which are similar to C++ templates) and Traits.

The Result type

One of the features of Rust is how important it considers proper error checking. In languages with try/catch (try/except in Python), one might be inclined to just wing it by enclosing stuff that might not work in a try block. The downside is that, if you don’t properly look at the resulting exceptions, you’ll never find out what is going wrong. Rust’s approach is to use the Result type. This is used to return not only the standard function output, but also a status of success or failure. There are multiple ways to process a Result type – match, unwrap, expect, and ?. I strongly recommend using Result in your own code – it might take a little bit of getting used to in the beginning, but it is the idiomatic way to write Rust code.

The Option type

The Option type is quite similar to Result. Rust does not allow for null/None variables in variables. Instead, if something might or might not contain or return a value, this has to be done via Option. This then requires the same treatment as a Result type, forcing you to actually think about what your code does instead of using null (or None in Python) as catch-all for undefined behavior.

Tests

I’ve written about it in my introductory Rust article, and I have to repeat it here: one awesome feature of Rust is how easy it is to write and execute tests, no complicated test framework required. This lowers the threshold for test-driven development a lot. I highly suggest to write both unit and integration tests. Yes, they’ll force you to think about what your code is supposed to do instead of just hoping that it works, but your code will be much more reliable. Writing good tests is not easy, though, especially with code that processes data.

Functional programming

For someone raised on if, for, and while, this is certainly the most foreign and complicated aspect of Rust. Not being used to it, I find functional code difficult to read and tend to avoid things like lambda functions in Python. But for those who are not as narrow-minded as I am, Rust has got you covered.

 

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert