Exploring Vectors in Mojo

Alex Forger
4 min readJun 12, 2023

--

Vectors are a fundamental data structure in programming that allow us to store and manipulate collections of elements efficiently. In the Mojo programming language, there are several vector implementations available that cater to different needs. In this blog post, we will explore three vector types: DynamicVector, InlinedFixedVector, and UnsafeFixedVector, and see how they can be used.

DynamicVector

The DynamicVector is a versatile vector implementation that dynamically allocates memory to store elements. It can resize itself as needed to accommodate more elements. Let's see an example of how to use DynamicVector:

from Vector import DynamicVector

# Create a DynamicVector of integers with an initial capacity of 8
var vect = DynamicVector[Int](8)
# Print the length of the vector
print(vect.__len__()) # Output: 0
# Add elements to the vector
vect.push_back(1)
vect.push_back(2)
vect.push_back(3)
vect.push_back(4)
vect.push_back(5)
# Print the updated length of the vector
print(vect.__len__()) # Output: 5
# Access and print an element at index 1 of the vector
print(vect.__getitem__(1)) # Output: 2
# Update an element at index 1 of the vector
vect.__setitem__(1, 10)
# Print the updated element at index 1 of the vector
print(vect.__getitem__(1)) # Output: 10
# Create a copy of the vector using the copy constructor
var vect_copy = vect.__copyinit__(vect)
# Clear the original vector
vect.clear()
# Print the length of the original vector after clearing
print(vect.__len__()) # Output: 0
# Add elements to the original vector again
vect.push_back(1)
vect.push_back(2)
vect.push_back(3)
vect.push_back(4)
vect.push_back(5)
# Add another element to the original vector
vect.push_back(5)
# Print the updated length of the original vector
print(vect.__len__()) # Output: 6
# Remove the last element from the original vector
vect.pop_back()
# Print the updated length of the original vector after removing the last element
print(vect.__len__()) # Output: 5
# Reserve additional capacity for the original vector
vect.reserve(10)
# Print the updated length of the original vector
print(vect.__len__()) # Output: 5
# Print the capacity of the original vector
print(vect.capacity) # Output: 10
# Print the size of the original vector
print(vect.size) # Output: 5

In the above code snippet, we create a DynamicVector with an initial capacity of 8. We can add elements to the vector using the push_back method, which dynamically resizes the vector if needed. The __len__ method returns the current length of the vector, and the __getitem__ and __setitem__ methods allow us to access and update elements at specific indices. We can also create a copy of the vector using the __copyinit__ method, clear the vector using clear, and perform other operations such as reserving additional capacity and retrieving the capacity and size of the vector.

InlinedFixedVector

The InlinedFixedVector type is a vector implementation that provides small-vector optimization. It allocates memory statically for a fixed number of elements, which eliminates the need for dynamic memory allocation for small vectors. Let's see an example of how to use InlinedFixedVector:

from Vector import InlinedFixedVector

# Create an InlinedFixedVector of integers with a capacity of 10
var vect = InlinedFixedVector[5, Int](10)
# Get the length of the vector
print(vect.__len__()) # Output: 0
# Append elements to the vector
vect.append(1)
vect.append(2)
vect.append(3)
vect.append(4)
vect.append(5)
# Get the updated length of the vector
print(vect.__len__()) # Output: 5
# Access and print an element at index 1 of the vector
print(vect.__getitem__(1)) # Output: 2
# Update an element at index 1 of the vector
vect.__setitem__(1, 10)
# Print the updated element at index 1 of the vector
print(vect.__getitem__(1)) # Output: 10
# Create a copy of the vector using the copy constructor
var vect_copy = vect.__copyinit__(vect)
# Clear the original vector
vect.clear()
# Print the length of the original vector after clearing
print(vect.__len__()) # Output: 0
# Append elements to the original vector again
vect.append(1)
vect.append(2)
vect.append(3)
vect.append(4)
vect.append(5)
# Print the updated length of the original vector
print(vect.__len__()) # Output: 5

In the above code snippet, we create an InlinedFixedVector with a capacity of 10. The append method is used to add elements to the vector, and the __len__, __getitem__, and __setitem__ methods work similarly to the DynamicVector. We can also create a copy of the vector using the __copyinit__ method, clear the vector using clear, and perform other vector operations.

UnsafeFixedVector

The UnsafeFixedVector type is a vector implementation that does not resize or implement bounds checks. It is suitable for scenarios where the number of elements is known at runtime and guaranteed to be within a certain capacity. Let's see an example of how to use UnsafeFixedVector:

from Vector import UnsafeFixedVector

# Create an UnsafeFixedVector of integers with a capacity of 10
var vect = UnsafeFixedVector[Int](10)
# Get the length of the vector
print(vect.__len__()) # Output: 0
# Append elements to the vector
vect.append(1)
vect.append(2)
vect.append(3)
vect.append(4)
vect.append(5)
# Get the updated length of the vector
print(vect.__len__()) # Output: 5
# Access and print an element at index 1 of the vector
print(vect.__getitem__(1)) # Output: 2
# Update an element at index 1 of the vector
vect.__setitem__(1, 10)
# Print the updated element at index 1 of the vector
print(vect.__getitem__(1)) # Output: 10
# Create a copy of the vector using the copy constructor
var vect_copy = vect.__copyinit__(vect)
# Clear the original vector
vect.clear()
# Print the length of the original vector after clearing
print(vect.__len__()) # Output: 0
# Append elements to the original vector again
vect.append(1)
vect.append(2)
vect.append(3)
vect.append(4)
vect.append(5)
# Print the updated length of the original vector
print(vect.__len__()) # Output: 5

In the above code snippet, we create an UnsafeFixedVector with a capacity of 10. The append method is used to add elements to the vector, and the __len__, __getitem__, and __setitem__ methods provide access to the vector elements. Unlike the previous vector types, UnsafeFixedVector does not resize or perform bounds checks, so it's important to ensure that the number of elements stays within the specified capacity.

These vector types in Mojo offer flexibility and performance optimizations depending on your requirements. Whether you need dynamic resizing, small-vector optimization, or a fixed-capacity vector without bounds checks, Mojo has you covered. Happy coding with vectors!

--

--

Alex Forger
Alex Forger

No responses yet