Rust for Python Programmers
Eric Greene November 13, 2023
Rust and Python
Rust is a system-level programming language that is rapidly growing in popularity and is loved by the programmers who use it. Take a quick look at the recent 2023 Stack Overflow Developer Survey. Their new chart on Desired/Admired programming languages shows how much programmers love Rust. While Python is more widely used, programmers do not love programming in Python as much as they love Rust.
Recently, Microsoft announced that they are now including Rust code in the Windows Kernel, and Rust will be replacing the C and C++ programming languages when non-garbage collected languages are needed.
While Python is a very popular and easy-to-use programming language, it has limitations such as slow execution, inefficient use of memory, and limited parallelism because of the GIL. Unlike Python which is interpreted, Rust is compiled to native code with C-like execution speed, efficient memory use, memory safety, and true parallelism.
Python Code
Let's consider the following Python code. This Python code provides a loop for capturing user commands from a console application. Programming a looped command entry mechanism is easy in Python.
=
break
The while True:
statement infinitely runs the loop until the break
statement executes. The input
function displays a prompt "> " and waits for the user to input some text. If the user types "exit", the loop will break, and the program will terminate. If the user types any other command, the command is displayed on the screen with the print(f"echo: {command}")
statement, and the loop continues.
This is very simple, yet useful code snippet, to get started with any command-driven console application. How would the above Python code be written in Rust?
Rust Code
To explore how the above Python code is written in Rust first make sure you have Rust installed.
The Rust code below is one way of implementing the above Python code. As you can see, the code is much longer, and there are more details the code must consider. Lower-level languages require more specific details from the programmer. With Python, things just work but work slowly, with lower-level languages like Rust, the programmer takes on more responsiblity to make things work. What is the benefit? Better performance, more control, etc.
use io;
use Write;
Rust Code Walkthrough
Unlike Python, Rust does not use whitespace to organize code blocks. Instead, Rust uses curly brace syntax style (like C, C++, C#, Java, JavaScript, etc.). Nevertheless, the standard code indent size is 4 spaces, just like Python. Also, similar to Python, many control-flow statements, such as if
statements, don't use parentheses around their expressions either.
Bring Types into Scope
In Python, the print
and input
are built-in functions that require no import
statements. Rust has something similar to the built-ins that is called the prelude. The prelude automatically brings certain types from Rust's standard library into scope. The stdout
and stdin
types are not part of the prelude, so the use
statements will bring these types into scope.
use io;
use Write;
Main Function
Unlike Python programs that can contain code statements without a main
function, Rust requires a main
function to serve as the starting point of program execution.
Command Loop
An infinite loop is constructed in Python with the while True:
statement. An infinite loop can only exit with a break
statement. Rust has a special loop
construct for infinite loops. Like Python, infinite loops are exited with a break
statement.
loop
While there are other kinds of loops in Rust, having a dedicated syntax structure for infinite loops makes the code more readable. Python's while True:
kind of feels like a hack.
Print Command Prompt and Command
Python's print
function outputs text to the screen. In Rust, the statement print!
is not a function but a macro. The macro is replaced by code that will print the argument "> " to the screen. The stdout
flush
function forces the output to the screen before waiting to read user input. The unwrap
method is useful when you expect the operation to be successful and want to handle the result directly. However, it's important to note that using unwrap without appropriate error handling can lead to unexpected program termination if an error occurs.
print!;
stdout.flush.unwrap;
In addition to the print!
macro, a println!
macro will print a new line at the end of the string.
println!;
Declare a Variable
In Python, variables are not explicitly declared. Instead, variables are created when they are first assigned to. In Rust, the let
statement declares a variable. By default, variables are immutable in Rust. To make a variable mutable, the mut
keyword must be applied when declaring a variable.
let mut command = String new;
Variables can be re-declared and shadow the previously declared variable.
let mut command = String new;
// ...omitted...
let command = command.trim;
The type for the first declaration of command
is String
, and the type for the second declaration is &str
. Commonly, shadowing is used to avoid declaring a second variable when the only purpose of the second variable is to be type conversion from the earlier declaration of the variable. Assigning values of different types is supported in Python because of dynamic typing. Nevertheless, dynamic typing is not the same as re-declaring a variable, even if it looks similar in this simple example.
Dynamic typing determines the type of a variable at runtime as the program executes. It is not possible to know for sure the type of a variable at development time. Rust's ability to re-declare a variable still determines the variables type at development time. There is no runtime uncertainty about the type of the variable at any point in the code.
Read Data from the Console
Python provides the built-in input
function to collect user input. The input
function prints a prompt to the screen and waits for the user to type in something and press Enter
. In Rust, the stdin
function only reads data from the user; it does not display a prompt. When the command
variable is passed as an argument to the read_line
function, the modifier &mut
is applied to the argument. The command
variable is passed by reference; its value can be mutated within the function. Rust is known for memory safety and has stringent rules about when memory can be changed and who owns memory. Because reading data can result in error, the expect
function handles any errors.
.read_line
.expect;
stdin
If Statement
Similar to Python, Rust supports if
statements. Observe that the conditional expression is not enclosed in parentheses. Instead of the comparison operator ==
, the String
's eq
function performs the string comparison. If true, the break
statement will cause the program to exit the loop
mentioned earlier.
if command.eq
Conclusion
Rust is a powerful system-level language that continues to grow in popularity and usage throughout the tech world. Python is a higher-level language used for many purposes, such as task automation, web applications, and machine learning. Sometimes, a program written in Python needs a more robust language that employs modern programming language features such as memory-safety and parallelism as well as significant execution speed boost. For such situations, Rust may be an excellent alternative to Python.
While not covered in this post, it is possible to write some parts of a Python program in Rust and call the Rust code from Python similar to how Python can call code programmed in C. Perhaps there will be a future post on this topic.