What is a struct in Mojo?

One of the distinguishing features of modern systems programming languages is their ability to construct high-level, safe abstractions atop complex, low-level operations without any performance trade-offs. In Mojo, this is where the struct type shines. A Mojo struct is akin to a Python class, supporting methods, fields, operator overloading, and decorators for metaprogrammingMetaprogramming is a programming technique in which computer programs have the ability to treat other programs as their data. , among other features. However, there are notable differences between Python classes and Mojo structs.

Python classes are dynamic, allowing dynamic dispatch, monkey-patching,Monkey patching is a technique used to dynamically update the behavior of a piece of code at run-time. and runtime binding of instance properties. Mojo structs, in contrast, is static, binding at compile-time and offering no flexibility for runtime additions or changes. This static nature enhances performance while preserving safety and ease of use.

To illustrate the concept, let’s examine a simple definition of a struct in Mojo:

struct Test:
var x: Int ## Define an instance property 'x' of type Int.
var y: Int ## Define an instance property 'y' of type Int.
fn __init__(inout self, x: Int, y: Int):
## Constructor for Test instances.
## 'inout self' represents the instance being initialized.
## 'x' and 'y' are parameters for initializing the 'x' and 'y' properties.
self.x = x ## Assign the 'x' parameter to the 'x' property.
self.y = y ## Assign the 'y' parameter to the 'y' property.
print("Initialized Test with x =", self.x, ", y =", self.y) ## Print statement
fn __lt__(self, rhs: Test) -> Bool:
## Less-than comparison method for Test instances.
## 'self' is the current instance, and 'rhs' is the instance to compare against.
print("Comparing self: (x =", self.x, ", y =", self.y, ") with rhs: (x =", rhs.x, ", y =", rhs.y, ")") ## Print statement
return self.x < rhs.x or \
(self.x == rhs.x and self.y < rhs.y)
## Return true if 'x' of the current instance is less than 'x' of 'rhs'.
## If 'x' values are equal, compare 'y' values, and return true if 'y' is less.

Code explanation

  • Line 1: We define a struct in Mojo called Test. A struct is a composite data type that can encapsulate multiple values under a single name.

  • Lines 2–3: We declare two instance properties within the Test struct. x and y are of type Int. These properties will hold integer values when instances of the Test struct are created.

  • Line 5: We define a constructor for the Test struct. In Mojo, the constructor is named _init_. It takes three parameters:

    • inout self: This parameter represents the instance being initialized and is marked as inout, indicating that it can be modified within the constructor. The self parameter is a reference to the current instance of the struct.

    • x: Int: This parameter is of type Int and represents the value that will be assigned to the x property of the Test instance.

    • y: Int: Similar to x, this parameter represents the value for the y property.

  • Lines 9–10: Within the constructor, we assign the values of the x and y parameters to the x and y properties of the current Test instance, respectively. This initializes the instance with the values provided during object creation.

  • Line 13: We define a method within the Test struct. The method is named _lt_ and is used for comparing Test instances. It takes two parameters:

    • self: This parameter represents the current instance on which the method is called. It is a reference to the left-hand side of the comparison.

    • rhs: Test: This parameter represents the right-hand side of the comparison, another Test instance, against which the current instance is being compared.

  • Lines 17–18: This part of the method is the implementation of the less-than comparison. It checks if the x property of the current instance (self.x) is less than the x property of the right-hand side instance (rhs.x). If this condition is true, it returns true. If not, it checks whether the x properties are equal, and if so, it compares the y properties. If the x properties are equal and the y property of the current instance is less than the y property of the right-hand side instance, it also returns true. Otherwise, it returns false.

What does this tell us?

We can see in the code above that the Mojo struct members must be explicitly declared using var or let declarations. Unlike Python, Mojo's struct structure and contents are predetermined and unalterable during program execution. This means we cannot dynamically modify the attributes of a struct, such as adding or removing methods, during runtime. However, the static nature of structs brings significant advantages. It contributes to improved program performance, as the program precisely knows where to find the struct’s information and how to utilize it without any additional runtime overhead.

Furthermore, Mojo’s structs seamlessly integrate with features familiar to Python developers, such as operator overloading. This enables us to redefine how common mathematical symbols like + and - work with our custom data types. Additionally, it’s worth noting that the “standard types” in Mojo, such as Int, Bool, String, and even Tuple, are built using structs. This design choice grants us greater flexibility and control over our code, as these types are not hardwired into the language but are accessible as part of the standard toolbox.

Suppose we were using Python instead. This is what our code would have looked like instead:

# Define a Python class named Test.
class Test:
# Constructor (__init__) for Test instances.
def __init__(self, x, y):
self.x = x # Initialize the 'x' attribute with the provided 'x' parameter.
self.y = y # Initialize the 'y' attribute with the provided 'y' parameter.
print(f"Initialized Test with x = {self.x}, y = {self.y}") # Print statement
# Less-than comparison method (__lt__) for Test instances.
def __lt__(self, rhs):
# Print statement before comparison.
print(f"Comparing self: (x = {self.x}, y = {self.y}) with rhs: (x = {rhs.x}, y = {rhs.y})")
# Check if the 'x' attribute of the current instance is less than 'x' of the right-hand side instance (rhs).
if self.x < rhs.x:
return True
# If 'x' values are equal, compare the 'y' attributes.
elif self.x == rhs.x:
return self.y < rhs.y
# If neither condition is met, return False (greater or equal).
return False
# Example usage
a = Test(3, 4)
b = Test(3, 5)
# Compare two instances
print(a < b) # This will trigger the __lt__ method and print the comparison result.

As we can see:

  • Python classes don’t require explicit type annotations, and the variables are dynamically typed.

  • Python allows dynamic dispatch, monkey-patching, and runtime changes to classes and methods.

  • The code is more dynamic and flexible but may come with performance overhead.

While Python offers flexibility and ease of use, it operates differently from Mojo structs, which are more static and performance-oriented. The choice between the two depends on the specific project requirements and trade-offs.

By understanding the differences between these approaches, developers can make informed decisions and choose the right tool for the job, whether it’s Python's dynamic flexibility or Mojo's performance-oriented static nature.

Unlock your potential: Mojo fundamentals series, all in one place!

To deepen your understanding of Mojo, explore our series of Answers below:


Free Resources

HowDev By Educative. Copyright ©2025 Educative, Inc. All rights reserved