Python to Rust: Beginning

August 22, 2017
9 min. read

This post is part of the Python to Rust series.

Why Rust for Me

I’ve been programming in Python for quite a few years. It is a great language for getting things done. There are so many modules and batteries included. You can do most things by selecting the right package. However, no one will tell you that it is fast. Unless you are talking about the speed to write code.

I started looking at new languages to learn as a way of growing as a programmer. I began looking at both Go and Rust. In some ways these don’t compete at all and in other ways they directly compete. Both are C-like in syntax with braces. Rust is lower level and more complex.

Go is a language that is meant to be easy to write and quick to code, but yield a much higher performance than Python. This was Google’s answer to better performance code from from fairly new programmers who are less experienced. In 2007, the bulk of Google code was C++, Java, and Python. As multiple processors come into play, Python’s GIL starts to have issues, as well as a performance disadvantage compared to C++ and Java. Java is very verbose and much more effort to write than a Python. C++ allows you to entirely crash systems or open security holes with poor code.

Go wanted to get close to C++ speeds, with Python ease of programming. I think they largely succeeded with this. A goal was also to drastically reduce compile times. In fact, 45 minute compile times give you space to think about things like creating a new programming language. Or so the story goes about the history of Go.

Go was not a language to break new ground in programming language design. It is all about making programmers more efficient and large systems work better together. This is done by having the language fairly small so you can have it in your mind at one time. This also helps with compilation speed. For Google and many others, this was a success.

I starting solving problems on exercism.io in Python, then Go and Rust. I found that I enjoyed Rust better. It was harder to get a Rust program written, because I had to really think about what I was doing. But once it compiled, I was fairly sure it would work and the implementation was elegant. It did not have the runtime failures that are encountered in Python and Go. The other thing I was a real fan of is that much of Rust is written in Rust. There are only a few “blessed” types that you could not write in the language yourself. Go has many blessed types that are not creatable with standard data structures.

In general, when I see how something works in Rust, it makes sense from an elegant programming style. Things like the Option or Result types, which we will discuss later. Go almost gets there with the dual return for value and error. However, by allowing nil (like Python’s None) in the language, we keep open a range of errors where the programmer must explicitly check for value before using it. The iterators and general feel of how things are done in Rust resemble Python structure somewhat. It feels a little like home. Just an oddly looking and often complaining home.

Rust compiles slowly. This is a known issue and steps are being taken to improve. It will never be as fast as Go, as it is a much more complex language. However, the analysis and checks involved are like a more experienced programmer telling you what you did wrong (with usually excellent suggestions for improvement.) Rust’s idea of ownership and borrowing eliminates the need for garbage collection. This makes almost everything zero cost at runtime and easily multi-threaded.

The most important in my mind is that Rust provides guaranteed memory safety. This is as important for not crashing as it is for not leaking secure information due to a bug. There may be programmers that can write as secure code as Rust in C or C++, but the number is very small. Most C and C++ programmers spend not a small amount of time fixing bugs that Rust won’t even allow to exist. So some pain to get to these points is worthwhile in my mind.

I know I would be much faster writing code in Go than Rust, at least to start out. However, my goal is to grow. Thus, my path switched to Rust. This will be a sequence of posts comparing and contrasting Rust from the view of a primarily Python programmer. Hopefully, it will help Python programmers try out Rust and go through the thought growth caused by the mental gymnastics required. I am also excited to get into some low level embedded use eventually and Python to Rust integration. I think Rust is a great language to mesh with Python, for faster implementations when needed, but without the dangers of C for the inexperienced.

Community

One thing that Rust and Python share is a great community. I have been impressed with the attitude in general of letting Rust stand on its own merits and not allow disparaging of any other language choice. Also, many are very helpful with getting past the learning humps that everyone has to get past to become productive in Rust.

There is an established Code of Conduct for interaction on the Rust User forums and also enforced in the Rust subreddit. This is needed. Open Source Software suffers highly from pompous kings of their mountain. Some of it is understandable when they have decades of experience in software or Linux and repeatedly getting newbie questions. However, there is a difference between telling people to go to a certain site and learn, vs posting on Twitter with the “stupid pull request of the day”. You can educate or push off without belittling (and most likely eliminating that person from contributing in the future.)

I’ve been using GitHub for years. But it was mostly solo or small team work without some of the branching and rebasing and all of that. When I started helping contribute some exercises for exercism.io in Rust, Peter Tseng was very helpful in just a little tutoring in Git commands for things I had not done. I learned more to make better pull requests for them and felt good about contributing to open source.

Rust gets many things right and Community is one of them. Conversation is respectful and disputes are resolved quickly, as most know if it usually a misunderstanding, rather than a true malevolence.

Cargo

It is easy for me to say that if Cargo is not the best package manager out there, it is tied for the best. Other than the slight issue currently of name squatting, everything handling Rust dependencies works great.

Python has the idea of virtual environments and docker instances to deal with versioning issues of dependencies and the Python interpreter. This is completely handled in cargo, by just listing dependencies in a file. You will generally specify major and minor versions, but not patch number. Cargo.lock will store the exact versions you used to build the binary (library or executable). This is great if you find a breaking change in one of your dependencies.

We will talk more about cargo and uses as I get into actually running code. If you want to look at the crates available (the Rust term for a package), just browse through crates.io, which is the equivalent of Python’s Package Index.

Getting Started

The first difference you will see as a Python programmer is the C-style syntax. There are many curly braces. This allows closures, chaining and anonymous functions that would become tedious in an indention based language like Python. And yes, there are semicolons again. I find myself deleting semicolons at the end of my Python code and forgetting them at the end of Rust code.

This is already a lengthy post, so I will just finish up with a brief look at an slightly advanced ‘hello_world’. This will show a little how Rust and Python feel similar and are different.

The following is Python code (obviously Python 3, I don’t use legacy Python unless I have to) that will set val to 0, 1, 2 and 3, as it consumes the iterator generated by range. For each of these, we are formatting a string value to print and inserting the number into it.

for val in range(4):
    print("val is {}".format(val))

This is the equivalent Rust code. Instead of def we use fn for the function definition. Our colon becomes braces for both the function and for loop. Our range iterator is created with 0..4 that is similar to Python’s list addressing in syntax. This will yield the same 0, 1, 2 and 3.

The exclamation point at the end of println! indicates that this is a macro. Rust uses macros to provide more advanced functionality not baked into the Rust language itself. In this instance it allows the compiler to validate issues with formatting instead of a runtime panic.

fn main() {
    for val in 0..4 {
        println!("val is {}", val);
    }
}

We do not need the call to main, because every executable Rust project must have a main() function as the point of entry into running code. If this were a library, there would be no required start point, as it only executes when you call in.

IDE

I use PyCharm for Python and started with that for Rust. The Rust plugin has updates almost weekly, it seems. I heard about the Rust Language Server (RLS) and decided to try that in VS Code. I used it for a few weeks and it was better. However, I didn’t realize that the Rust plugin had improved so much in that time as well. I’m now back using Rust in PyCharm. (This plugin works on any IntelliJ based IDE.)

I should also mention that I do most of my Python and Rust on Windows. A large part of my day job involves expensive software that is only available on Windows. For the most part, Python has been a first class citizen on Windows and Rust works just as well. It looks like we are currently on the edge of debugging on Windows. As I learn how to do that, I will post about it.

Follow along

If you wish to follow along in code, as I compare and contrast Python and Rust, you may want to install Rust. Once installed, updating to new versions is done with the rustup tool itself.

You also have the option of using the web based Rust Playground for running code on any system with a browser. You can also create a Gist from here and get a link back to the playground with the gist. This is a common way to ask for help with a given section of code, as it makes a runnable environment for those trying to help with no work on their part.

In addition to what I’m typing here, there are many great (much better than mine) learning resources for Rust. The Rust Book has both the 1st edition and in progress 2nd edition of the text. I also learned via Rust by Example.


Part 1 of 4 in the Python to Rust series.

Python to Rust: PIP to Cargo

comments powered by Disqus