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
Python classes are dynamic, allowing dynamic dispatch, 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 statementfn __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 statementreturn 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
structin Mojo calledTest. 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
Teststruct.xandyare of typeInt. These properties will hold integer values when instances of theTeststruct are created.Line 5: We define a constructor for the
Teststruct. In Mojo, the constructor is named_init_. It takes three parameters:inout self: This parameter represents the instance being initialized and is marked asinout, indicating that it can be modified within the constructor. Theselfparameter is a reference to the current instance of the struct.x: Int: This parameter is of typeIntand represents the value that will be assigned to thexproperty of theTestinstance.y: Int: Similar tox, this parameter represents the value for theyproperty.
-
Lines 9–10: Within the constructor, we assign the values of the
xandyparameters to thexandyproperties of the currentTestinstance, respectively. This initializes the instance with the values provided during object creation. -
Line 13: We define a method within the
Teststruct. The method is named_lt_and is used for comparingTestinstances. 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, anotherTestinstance, 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
xproperty of the current instance (self.x) is less than thexproperty of the right-hand side instance (rhs.x). If this condition is true, it returnstrue. If not, it checks whether thexproperties are equal, and if so, it compares theyproperties. If thexproperties are equal and theyproperty of the current instance is less than theyproperty of the right-hand side instance, it also returnstrue. Otherwise, it returnsfalse.
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 usagea = Test(3, 4)b = Test(3, 5)# Compare two instancesprint(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:
What are the fundamentals of Mojo?
Understand the basics of Mojo, its design principles, and how it enhances performance for AI and system-level programming.What’s the difference between
intandIntin Mojo?
Learn how Mojo differentiates between built-in primitive types and custom types with enhanced functionality.What’s the difference between
fnanddefin Mojo?
Discover when to usefnfor performance-oriented functions anddeffor flexible, dynamic programming.What’s the difference between
letandvarin Mojo?
Explore the distinction betweenlet(immutable) andvar(mutable) variables and their impact on memory safety.MLIR in Mojo
Understand how Mojo leverages Multi-Level Intermediate Representation (MLIR) for optimizing AI workloads and low-level computations.What is argument mutability in Mojo?
Learn how Mojo handles mutable and immutable function arguments to enhance performance and safety.What is a struct in Mojo?
Explore how structs in Mojo provide efficient, memory-safe data structures for performance-critical applications.Mojo vs. Python
Compare Mojo and Python in terms of speed, memory efficiency, and usability for AI and system-level programming.
Free Resources