Rust for Python Programmers #5 — Lists

Juliano Fischer Naves
4 min readSep 3, 2023

--

Welcome back, fellow Pythonistas-turned-Rust enthusiasts! In this edition of “Rust for Python Programmers,” we’re shifting our focus to another fundamental data type: lists. While Python’s lists are a staple for any Python programmer, Rust offers alternatives with its array and vector types. Let’s dive into the world of lists, exploring operations like insertion, removal, slicing, and indexing in both languages while unraveling new Rust concepts.

Lists in Python

In Python, lists are dynamic arrays that can hold elements of different types. They’re versatile, allowing you to perform a variety of operations easily:

my_list = [1, 2, 3, 4, 5]
# Insertion
my_list.append(6)
my_list.insert(2, 7)
# Removal
my_list.remove(3)
popped_element = my_list.pop()
# Slicing
sublist = my_list[1:4]
# Get by index
element = my_list[0]

Rust’s Array and Vector

Rust offers two primary alternatives to Python lists: arrays and vectors.

Arrays

Arrays in Rust are fixed-size, making them more like Python tuples. They’re declared like this:

let my_array = [1, 2, 3, 4, 5];
// Insertion (not possible with arrays)
// Removal (not possible with arrays)
// Slicing
let slice = &my_array[1..4];
// Get by index
let element = my_array[0];

Arrays are stack-allocated, which means their size is determined at compile-time. They’re suitable for cases where the size is known and fixed.

Vectors

Vectors are dynamic arrays in Rust, similar to Python lists, and are part of Rust’s standard library.

use std::vec::Vec;
let mut my_vector: Vec<i32> = vec![1, 2, 3, 4, 5];
// Insertion
my_vector.push(6);
my_vector.insert(2, 7);
// Removal
my_vector.remove(3);
let popped_element = my_vector.pop();
// Slicing
let slice = &my_vector[1..4];
// Get by index
let element = my_vector[0];
  • use std::vec::Vec;: This line imports the Vec type from Rust's standard library to use it in your code.
  • Vec<i32>: This declares a new Vec containing 32-bit signed integers (i32). Rust requires specifying the type contained in the Vec.
  • vec![1, 2, 3, 4, 5]: This macro creates a new Vec with the specified elements.
  • &my_vector[1..4]: The & symbol indicates that we're taking a reference to a slice of the Vec. Rust's indexing is zero-based, so [1..4] extracts elements at indices 1, 2, and 3.

In Rust, you need to declare variables as mutable (mut) if you want to modify them, enforcing a strong sense of ownership and mutability that helps prevent common programming errors.

Vectors are heap-allocated and can grow or shrink dynamically, making them suitable for situations where the size is not known in advance or needs to change over time.

Heterogeneous Vector

In Rust, vectors (represented by Vec<T>) are homogeneous, meaning they can only contain elements of a single type T. Unlike Python lists, you cannot have a Rust vector that directly contains elements of different types, such as ints, floats, and strings, in the same vector.

However, you can achieve similar functionality by using Rust’s enum to create an enum that can hold different types of data.

An enum in Rust is a custom data type representing a type with one of several values. In this case, MyEnum is an enum type that can have three different variants: Integer, Float, and Text. Each variant can hold a different type of data.

fn main() {
// Creating instances of MyEnum variants
let int_value = MyEnum::Integer(42);
let float_value = MyEnum::Float(3.14);
let text_value = MyEnum::Text(String::from("Hello, Rust!"));

// Accessing values inside enum variants
match int_value {
MyEnum::Integer(val) => println!("Integer: {}", val),
_ => (),
}

match float_value {
MyEnum::Float(val) => println!("Float: {}", val),
_ => (),
}

match text_value {
MyEnum::Text(val) => println!("Text: {}", val),
_ => (),
}
}
  1. Integer(i32): This variant represents an integer value of type i32. It can hold signed 32-bit integer values.
  2. Float(f64): This variant represents a floating-point value of type f64. It can hold 64-bit double-precision floating-point values.
  3. Text(String): This variant represents a text value of type String. It can hold a string of characters.

You can then create a vector of that enum type, effectively creating a heterogeneous container. Here's an example:

enum MyEnum {
Integer(i32),
Float(f64),
Text(String),
}

fn main() {
let mixed_vector: Vec<MyEnum> = vec![
MyEnum::Integer(42),
MyEnum::Float(3.14),
MyEnum::Text("Hello, Rust!".to_string()),
];

// Accessing elements
for item in &mixed_vector {
match item {
MyEnum::Integer(val) => println!("Integer: {}", val),
MyEnum::Float(val) => println!("Float: {}", val),
MyEnum::Text(val) => println!("Text: {}", val),
}
}
}

In this example, we define an enum MyEnum that can hold different types of data: integers, floats, and strings. We then create a Vec<MyEnum> to store instances of this enum, effectively creating a vector that can hold different types.

So, while Rust vectors themselves are homogeneous, you can use enums to create heterogeneous collections, and you can have vectors of vectors to r and ns.

Vector of Vectors

Rust allows you to create vectors of vectors. You can have a Vec<Vec<T>>, where each inner vector can hold elements of the same type T. Here's an example:

fn main() {
let vector_of_vectors: Vec<Vec<i32>> = vec![
vec![1, 2, 3],
vec![4, 5],
vec![6, 7, 8, 9],
];

// Accessing elements
for inner_vector in &vector_of_vectors {
for element in inner_vector {
print!("{} ", element);
}
println!();
}
}

In this example, vector_of_vectors is a vector of vectors containing integers (i32), and you can access and manipulate the inner vectors as needed.

Wrapping Up

In summary, while Python’s lists are dynamic and flexible, Rust’s arrays and vectors offer precise control, safety, and performance. Although Rust Vectors are homogeneous, we can achieve a kind of heterogeneous vector by using enums. We can also have vectors of vectors and this may be useful for Python programmers since it is a common construction in Python code. On the other hand, Rust arrays are more comparable to Python tuples since they are fixed-sized.

--

--