Python Idioms in Rust
Tags: python, rust, programming
Published: 2018-03-24 01:20:27

---
title: Python Idioms in Rust
published: true
tags: python,rust,programming
canonical_url: http://benjamincongdon.me/blog/2018/03/23/Python-Idioms-in-Rust/
cover_image: https://i.imgur.com/adqM8Ed.jpg
---

I’ve been going through a period of programming language wanderlust over the past couple months. Recently, I’ve been quite interested in Rust. Coming from Python, I’ve found a lot of Rust’s language features to be quite powerful.

Earlier this year, I dug deep into Go. For the most part, I enjoy Go: It’s a simple language that can be learned in a few hours (literally). Its structure promotes good programming practices and it has a wonderful ecosystem.

However, in Go, I felt limited by the expressiveness of the language. Its much-bemoaned lack of generics is less disruptive than one would expect but makes clever functional-programming tricks essentially impossible. That’s fine — it’s not what Go was designed for — but as a result, I found Go a bit less “fun” to work in.

While Rust does have additional complications coming from it’s [infamous borrow checker](https://m-decoster.github.io//2017/01/16/fighting-borrowchk/) and the complexity of generics, I’ve found that what you gain in expressiveness is worth the tradeoff.

In fact, I found Rust to be rather Pythonic<sup id="fnref:1"><a href="#fn:1">1</a></sup>. ✨ Sure, you have to be careful about types and ownership, but there is a strong case to be made that Rust shares many of the strengths of Python’s syntax.

After doing a bit of work in Rust, I identified some idioms<sup id="fnref:2"><a href="#fn:2">2</a></sup> common to Rust and Python. These common features are all things that I enjoy using in Python, and I’ve found to be useful while coming up-to-speed on Rust.

## Enumerate

A common pattern in when iterating over lists in Python is to use `enumerate` to have get both the index and value of list elements:

```python
arr = ['a', 'b', 'c']
for idx, val in enumerate(arr):
    print("{}, {}".format(idx, val))
# 0, a
# 1, b
# 2, c
```

Rust iterators also support `enumerate`! (And you don’t even have to learn a new name for the same functionality)

```rust
let arr = vec!['a', 'b', 'c'];
for (idx, val) in arr.iter().enumerate() {
    println!("{}, {}", idx, val);
}
```

## Zip

Another function on iterators that you see pretty frequently in Python is `zip`, which combines 2 iterators element-wise, creating an iterator of tuples with an element of each list.

```python
letters = ['a', 'b', 'c']
numbers = [1, 2, 3]
for l, n in zip(letters, numbers):
    print("{}, {}".format(l, n))
# a, 1
# b, 2
# c, 3
```

Again, Rust iterators also support `zip`:

```rust
let letters = vec!['a', 'b', 'c'];
let numbers = vec![1, 2, 3];
for zipped in numbers.iter().zip(letters) {
    println!("{:?}", zipped);
}
// (1, 'a')
// (2, 'b')
// (3, 'c')
```

Like Python’s `zip`, Rust’s `zip` halts when either of the iterators is exhausted. This allows you to do cool tricks with infinite iterators:

```rust
let start_at_10 = 10..;
let letters = vec!['a', 'b', 'c'];
for zipped in start_at_10.zip(letters) {
    println!("{:?}", zipped);
}
// (10, 'a')
// (11, 'b')
// (12, 'c')
```

## Tuples

Rust has tuples! This is a feature that I sorely missed in Go. Yes, `struct`s work just as well, but for “scripting” or prototype work, tuples are a great language feature.

Python’s tuple system is pretty flexible. Functionally, Python tuples act like immutable lists. Tuples can be aliased into separate variables via unpacking, and are indexed-into like lists.

```python
foo = ('foo', 1, None)
a, b, c = foo
assert a == 'foo'
assert foo[0] == 'foo'

# Tuples are immutable in Python
tup = (1, 2, 3)
tup[0] = 4
# TypeError: 'tuple' object does not support item assignment
```

Rust’s [tuple feature](https://rustbyexample.com/primitives/tuples.html) is also quite powerful. Similar to Python, you can alias tuple values into separate variables using syntactic sugar, although in Rust this is called “destructuring” instead of “unpacking”.

```rust
let foo = ("foo", 1, None::<i32>);
let (a, b, c) = foo;
println!("{}", a); // "foo"
println!("{}", foo.0); // "foo"
```

Also, Rust tuples aren’t indexed using square brackets (`foo[0]`), but rather by the dot operator (`foo.0`). This is a byproduct of the fact that Rust tuples are sized at compile-time, unlike Python tuples which are dynamic.

```python
# Dynamically sized tuples in Python
tup = tuple(['foo' for _ in range(randint(1, 10))])
```

Unlike in Python, Rust tuples can be made mutable:

```rust
let mut tup = (1, 2, 3);
tup.0 = 4;
```

## List Comprehensions (kind of)

List comprehensions are the bread-and-butter of Python syntactic sugar. It’s a super expressive way of generating/filtering lists.

```python
evens_squared = [x**2 for x in range(10) if x % 2 == 0]
# [0, 4, 16, 36, 64]
```

Rust doesn’t have list comprehensions _per se_, but Rust iterators _do_ have `map` and `filter`, which allow you to perform similarly expressive “list” generation:

```rust
let evens_squared = (0..10)
    .filter(|x| x % 2 == 0)
    .map(|x| x * x)
    .collect();

println!("{:?}", evens_squared);
// [0, 4, 16, 36, 64]
```

Personally, I like Rust’s extensive iterator functions _even better_ than Python’s. I found functions like [flat\_map](https://doc.rust-lang.org/1.15.1/std/iter/trait.Iterator.html#method.flat_map) and [scan](https://doc.rust-lang.org/1.15.1/std/iter/struct.Scan.html) (among others) to be quite useful. These iterator functions bring aspects of functional programming to Rust that I really enjoy.

## Single-Line If

Neither Python nor Rust has [ternary operators](https://en.wikipedia.org/wiki/Ternary_operation) (which may be for the best). However, they both support single-line conditional expressions. This can be useful in certain conditions, but it’s really just syntactic sugar.

```rust
result = expensive_function() if some_condition else None

let result = if some_condition { expensive_function() } else { 0 }
```

## Lambda Functions

An oft complained-about language feature of Python is its `lambda` functions. They’re useful, but the syntax isn’t great. Other languages like Ruby and Javascript have more terse syntax for specifying closures or inline functions.

Python `lambda`s take a set of arguments and return the evaluation of the expression that follows the `:`

```python
arr = [3, 10, 15]
sorted(arr, key=lambda x: abs(10-x))
# [10, 15, 3]
```

Rust closures are similarly expressive. They use pipes (`|`) to hold arguments and the expression that follows is used as the return value.

```rust
let mut arr: Vec<i32> = vec![10, 15, 3];
arr.sort_by_key(|a| (10 - a).abs());
```

Rust closures can also be multiline functions using brackets, which is quite handy:

```rust
let mut arr = vec![10, 15, 3];
arr.sort_by_key(|a| {
    let intermediate_value = some_function(a);
    intermediate_value + 10
});
```

Both Python `lambda`s and Rust closures perform variable capture, so you can have access to variables that are in-scope where the lambda/closure is created:

```python
a = 10
plus_a = lambda x: x + a
plus_a(5)
# 15
```
```rust
let a = 10;
let plus_a = |x| x + a;
plus_a(5); // 15
```

## First-Class Functions

In Python, functions are essentially objects that are invoked with the ` __call__ ` magic function. They can be passed around like any other object and can even have attributes assigned to them. This can be useful for passing callback functions. While idiomatic Python doesn’t make extensive use of functions-as-variables, this feature comes in handy pretty often.

```python
def plus_one(x):
    return x + 1

def map_on_list(arr, func):
    return [func(x) for x in arr]

arr = [1, 2, 3]
map_on_list(arr, plus_one)
# [2, 3, 4]
```

Rust also has first-class functions. Functions and closures can be used as argument to other functions:

```rust
fn plus_one(x: i32) -> i32 {
    x + 1
}

fn map_on_vec(vec: &Vec<i32>, func: fn(i32) -> i32) -> Vec<i32>
{
    let mut new_vec = Vec::new();
    for i in 0..vec.len() {
        new_vec.push(func(vec[i]));
    }
    new_vec
}

let vec = vec![1, 2, 3];
println!("{:?}", map_on_vec(&vec, plus_one));
// [2, 3, 4]

// Equivalently:
println!("{:?}", map_on_vec(&vec, |x| x + 1));
```

Interestingly, there are some subtle differences between Rust functions and closures. <sup id="fnref:3"><a href="#fn:3">3</a></sup> In general, they can be treated as the same thing. Of course, functions are _not_ objects in Rust, but a Rust struct can implement the [Fn](https://doc.rust-lang.org/std/ops/trait.Fn.html), [FnMut](https://doc.rust-lang.org/std/ops/trait.FnMut.html), or [FnOnce](https://doc.rust-lang.org/std/ops/trait.FnOnce.html) traits to similar effect of Python’s ` __call__ ` (though this is [not recommended](https://stackoverflow.com/a/38672502/2421634)).

## Conclusion

Hopefully, I’ve given a bit of a hint as to why a Python dev might be interested in picking up Rust (for aesthetic reasons, if nothing else!). There’s a definite learning curve to Rust, but it’s a speedy language that still remains quite expressive.

From a syntactic sense, I found Rust to be more similar to Python than Python is to Go. Using generics and type inference, you can end up writing Rust code that has only minimal “type boilerplate” and _feels_ pretty similar to Python.

If you’re interested in looking into Rust, I’d highly recommend the following resources:

- [The Rust Programming Language](https://doc.rust-lang.org/book/second-edition/) - Official (free) book that walks you through the Rust language
- [Rust Language Documentation](https://doc.rust-lang.org/std/) - The API docs of the Rust language — it’s search functionality is pretty great
- [/r/rust](https://www.reddit.com/r/rust/) - The Rust Subreddit

_Cover: [Pixabay](https://pixabay.com/en/crab-baskets-scotland-fishing-2550284/)_

## Footnotes

<span id="fn:1">1.</span> [Armin Ronacher](https://twitter.com/mitsuhiko) did a [great talk](https://www.youtube.com/watch?v=zmtHaZG7pPc) about pairing Rust and Python that expands upon this notion of Rust being “Pythonic”

<span id="fn:2">2.</span> Some of these are probably better defined as syntactic sugar or language features. 😛

<span id="fn:3">3.</span> Ricardo Martins wrote a [blog post](https://ricardomartins.cc/2015/10/12/practical_differences_between_rust_closures_and_functions) about this distinction between functions and closures