bkmr
Super fast CLI Bookmark Manager and Launcher
Developer Productivity in Rust vs Python
I am aware that this is a complex subject and impossible to treat objectively, especially given my time limit of 3 weeks.
In order to compare apples with apples I decided to re-implement a Python productivity application in Rust, which I use on a daily basis:
CLI bookmark manager with fulltext search capability: twbm.
Objectives:
- Learn Rust and it’s idioms
- Get an idea of developer productivity in Rust vs Python
- Get an understanding of Rust’s capabilities and ecosystem
- Better performance for a frequently used CLI tool
Approach: 1-to-1 translation of Python CLI bookmark manager twbm to Rust.
First Impression
Rust’s syntax looks dense and sometimes verbose, harder on my eyes than Python. I like the Python bracket-less style and the almost natural language syntax. One of Python strengths.
Python feels like writing a letter, while in Rust, it felt more like compiling a legal contract.
I also am missing things which I would take as a given in modern languages, e.g.
- keyword arguments
- default parameters
- exceptions
I read quite a view articles in this regard and I gained the impression that missing features are being justified in the community with ’explicitness’ and cleanliness of the language and are ‘by design’.
However, I do not need to be persuaded that missing features are actually good for me and helping me writing clean code. I can decide by myself and like to have the choice.
And it’s not like there is no hidden magic in Rust: Implicit Deref coercion was added to Rust so that programmers writing function and method calls don’t need to add as many explicit references and dereferences with & and *.
E.g.: hello(&(*m)[..]);
The Hard and the Ugly
I went through Rust’s notorious baptism of fire, trying to work with, against and around the Rust compiler and borrow-checker. New paradigms can be hard, that’s part of the experience. The promise that code once it compiles runs flawlessly helps a lot.
I have to say though, that traditional debugging comes more naturally to me than trying to understand all the compile errors in Rust up front. There is no compiler debugging, so the only way to find the problem is based on compiler messages. No step debugging or variable inspections here to help narrowing down the problem.
I like to refactor heavily during development, but with the compiler looming, every non trivial change felt like a threat. Every change guaranteed having another heavy compiler fixing session. This motivates to avoid refactoring which is not a good thing. Maybe this goes away with more experience.
Language Features
I am a fan of Python’s list comprehensions and generators and found that Rust’s collections and iterators were a good equivalent here. Overall I think the two languages are pretty similar in language features on a higher level, except for the exceptions mentioned above.
On low level systems programming Python does not compete, of course.
Testing
Cargo as test runner does a decent job but I feel that fixture- and parametrized-based testing
is a minimum requirement nowadays. Here rstest
is a start, but no match to pytest
.
Ecosystem, 3rd Party Libraries
In Python world almost all problems have been solved already in high quality. Rust seems to be catching up quickly and I could find everything I needed:
diesel
as ORMclap
for CLIskim
for fzf style search- …
Setting up and managing a project with Cargo is a pleasure. Dependency management feels much more pleasant than in Python.
Documentation is excellent in quality and structure. This helps to sail the complexities and idioms of Rust. The entire setup reminds of Golang which is similar enjoyable.
Performance
No suprises here. The application in Rust performs ~20s faster in comparison to Python, even when Python is warmed-up. A nice reward for a lot of effort.
Rust excels in performance, especially in startup time for CLI applications.
On the other hand Python’s syntax is clearer. Readability is sometimes more important than raw performance, which is ‘good enough’ for many Python applications.
Teaser
Summary
Rust’s memory ownership model is interesting but it gets in your way often. It increases complexity and development time and it must be weighed against the benefits it provides. In languages like Golang, Java or Python a lot if this complexitity is handled by the Garbage Collector.
For a CLI application startup time matters, but for large applications and distributed systems, speed of execution is often less important than speed of development. Also Golang and Java have similar performance characteristics for long running applications.
It was an intensive christmas holiday and significant more effort than I expected. Still, I only could scratch the Rust surface.
My reward is a project which is 1:1 comparable to the Python version in terms of features, speed, readability and complexity.
And a performance gain of 20x for a tool I use daily.
The Rust source code is here: bkmr.