Rust for Python Developers: Environment Setup

Raja Sekar
7 min readMar 31, 2019

--

In my last article, I tried to make my case for learning Rust for Python developers or developers from other higher level programming languages. I have decided to contribute towards the goal of making Rust approachable for Python developers. I am writing a small series of Rust tutorials as time permits aiming at challenges I have faced while learning Rust and solutions I have found along the way.

This is my first article in the series of articles about making Rust approachable for Python developers and developers from other high-level languages. Here, I am going to describe my typical environment setup for programming in Rust.

I work exclusively on Linux environment. So I can’t suggest much for Windows and MacOS users. However, I guess most of these should be very similar in all these platforms.

Installing Rust:

In Linux and MacOs, installing Rust is as simple as executing the following line in the terminal.

curl https://sh.rustup.rs -sSf | sh

For Windows users, please follow this link:

https://rustup.rs/ (click on display all supported installers.).

Stable and Nightly Rust:

Rust has two major versions. One is a stable version and the other one is a nightly version. New language-related features get experimented and tested in nightly version and once it gets stabilized, those features will be moved to the stable version. Rust’s stable version provides typical stability guarantees like backwards compatibility, etc., Sometimes some dependencies might need a nightly version. To make either one default, run the respective commands as given below.

rustup default stablerustup default nightly

Cargo:

Creating a Cargo project:

Cargo is Rust’s amazing package manager and build tool. It downloads and builds all the required dependencies for your project. It is super easy to use. You would be amazed by its ease of use and wonder why such a tool is not available for some mainstream languages.

I am looking at you —> Go!!! (go mod has a long way to go)

Starting a new Rust executable application:

cargo new --bin project_name

Starting a new Rust library:

cargo new --lib project_name

In case of a binary project, Cargo would have automatically created a main.rs file inside your src/ folder. In case of a library project, it would have created a lib.rs file.

You can configure all project related settings in Cargo.toml like the following sample:

[package]
name = "aco_rust"
version = "0.1.0"
authors = ["raja"]
edition = "2018"
[dependencies]
memmap = "0.7.0"
serde_derive = "1.0.84"
fnv = "1.0.6"
byteorder = "1.2.7"
serde = "1.0.89"
rustbreak = "1.4.0"
rand = "0.6.5"
rayon = "1.0.3"
permutation = "0.2.5"
[profile.release]
debug = true

Rust project is called crate. You can publish your crate to https://crates.io/ which is similar to Python’s PyPI.

Building a Cargo Project:

I will come out and say at the beginning itself, Rust’s compile time is very slow. It has excellent incremental compilation though. So here is what I generally do for my project. I try to list down all possible dependencies in Cargo.toml file and let all of the following commands run and meanwhile start working on the project.

cargo checkcargo buildcargo build --release

Depending on the number of dependencies, it can take quite a while. From next time onwards, when you change something in your project, only those changes will be compiled, thereby reducing the build time significantly.

So what do those commands do?

cargo check — > It checks code correctness without actually building the code. It is very fast and can be used to check whether our code is correct or not while we are developing. As you will see in the following articles, reading compiler error codes is an integral part of writing Rust project. Rust is very unique in this regard. Unlike most languages where we dread the compiler, Rust compiler is our companion. We rely on it too much. So this command speeds up our workflow a lot.

cargo build — > It compiles the code without any optimization. It is called the debug mode. It is helpful in debugging stage where you don’t care about the speed of the program but want to check whether our code is working as intended. Beware, Debug executable is as slow as molasses compared to release mode. Please don’t measure the performance of Rust code in this mode. You will be really disappointed.

cargo build --release — > It compiles the code with optimizations enabled. Depending on applications, I have seen the difference to be as high as 30X compared to debug mode. Release mode compilation is very slow compared to the above two options. So typically, you invoke it when you are done with the whole project and want to measure the performance or for shipping it to production.

Both debug and release mode executables are stored inside their respective folders target/debug/project_name and target/release/project_name.

The above-mentioned method produces dynamically linked executable which means it will run only on machines similar to your machine and OS since it has the dependency of a particular system’s libc.

To produce fully static binaries which you can run on any machine, do the following.

rustup target add x86_64-unknown-linux-musl

For nightly version,

rustup target add x86_64-unknown-linux-musl --toolchain=nightly

Build the statically linked binary:

cargo build --release --target x86_64-unknown-linux-musl

The resulting binary is completely portable. Now you can take the binary and run it on any Linux based machine.

Refer the following GitHub project if you want to build docker images of static Rust binaries.

IDEs:

Vim + Tmux = ❤

I am most at home in Vim. Tmux and Vim satisfy most of my needs while working on a new project. Technically I use NeoVim. This is how my typical environment looks like:

NeoVim with Rust plugins
Tmux + Vim -> My Typical Rust Environment

Rust has a language server called RLS. It integrates well enough with vim plugin “ale”. It provides some functionalities like jump to definition, formatting the code, code completion, basic refactoring,etc., It also checks your code for error as you write the code.

Install the following cargo tools.

rustup component add rustfmtrustup component add rls rust-analysis rust-srccargo +nightly install racer

If you are using Vundle plug-in manager, adding the following to your vimrc or nvim/init.vim will setup the Rust environment for Vim/NeoVim.

Plugin 'racer-rust/vim-racer'
Plugin 'rust-lang/rust.vim'
Plugin 'w0rp/ale'
" change the bin path as your system settingslet g:racer_cmd = "/home/raja/.cargo/bin/racer"
let g:racer_experimental_completer = 1
let g:ale_python_auto_pipenv=1
let b:ale_linters = {
\ ‘python’: [‘pylint’],
\ ‘rust’: [‘rustup’, ‘run’, ‘nightly’, ‘rls’],
\ ‘go’: [‘go-langserver’] }

Clippy:

Clippy is Rust linter. While you are in learning phase, Clippy is extremely helpful. It identifies common mistakes and provides suggestions for modifying your code.

Install clippy using the following command:

rustup component add clippy 

You can run Clippy inside your project folder as follows:

cargo clippy

You can find all the Clippy lints here.

While this is enough for me most of the times, most developers prefer to use IDEs. There are good reasons for that. When projects become big enough, IDEs become more convenient when working with many developers. Vim + RLS can at sometimes become unreliable and buggy as the project size grows. Refactoring is also another major hindrance as far as I have observed in Vim.

VS-Code:

While technically not an IDE, it generally provides a good experience for me. It also utilizes rust-rls to provide typical IDE experience. So it can also be buggy sometimes.

All you have to do is to install official Rust (rls) plugin. You are good to go. VSCode also provides debugging functionality using CodeLLDB.

To debug Rust programs, install the following extensions.

CodeLLDBC/C++ extension

Change the program field in launch.json to your debug binary as follows.


"configurations": [
{
"name": "(gdb) Launch",
"type": "lldb",
"request": "launch",
"program": "path_to_your_project/target/debug/project_name",
"args": [],
"sourceLanguages": ["rust"],
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": true,
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
}
]
Rust in VSCode

IntelliJ Rust:

This is by far the most reliable IDE for Rust based on my experience. Code completion, jump to definition, refactoring, on the fly error analysis,etc., all work very well and it’s butter smooth. However, debugging is available only on CLion(paid version). You have to install Rust-plugin and it just works.

IntelliJ Rust

So overall, for a relatively small personal project, I tend to use Vim mostly. For large projects, I tend to use IntelliJ Rust while developing and use VSCode while debugging.

Extras:

  • Always format the Rust code using rustfmt before publishing it to GitHub. Just run cargo fmt inside your project directory.
  • Cargo watch — to run tests(and other cargo commands) when some code changes.
  • Cargo outdated — to display out of date dependencies.

--

--

Raja Sekar

Deep Learning practitioner, Distributed Systems enthusiast and a newbie entrepreneur