Learning Cairo - Optimizing Code Structure Using Structs (9)
Prerequisite
Install cairo-run.
Struct Example
In Learning Cairo - The Syntax of Structures (8), we have seen some Cairo programs using structs. In this article, let's see an example of optimizing code structure using structs.
Suppose we need a program to calculate the area of a rectangle, then we can easily write this code:
use debug::PrintTrait;
fn main() {
let width1 = 30;
let height1 = 10;
let area = area(width1, height1);
area.print();
}
fn area(width: u64, height: u64) -> u64 {
width * height
}
The area
function receives two arguments, width
and height
, calculates their product, and returns it. This code is fine, it does the job, but maybe we can improve it.
First, when we call the area
function, we need to remember the position of the two parameters, for example, we need to remember that the first parameter is width
and the second is height
(of course, in this area
function, it doesn't matter if the arguments are reversed). Second, we need to manually pass in all the parameters. If we need to calculate the area of a more complex shape, there may be many parameters, making it hard to read and manage.
We learned about the Tuple structure in previous articles, and we can try to refactor the code using Tuple:
use debug::PrintTrait;
fn main() {
let rectangle = (30, 10);
let area = area(rectangle);
area.print(); // print out the area
}
fn area(dimension: (u64, u64)) -> u64 {
let (x, y) = dimension;
x * y
}
We put both width
and height
into a tuple, which makes the area
function only take one parameter. For more complex parameters, we can also put them into a tuple to optimize parameter complexity. However, this brings another problem, the parameters of area
currently have no meaning, it's just a tuple, and its elements are not named. This makes it even less readable. When using the area
function, we not only need to remember the order of the parameters, but also have to carefully align the function parameters because the parameters have no names.
So let's refactor the code using structs:
use debug::PrintTrait;
struct Rectangle {
width: u64,
height: u64,
}
fn main() {
let rectangle = Rectangle { width: 30, height: 10, };
let area = area(rectangle);
area.print(); // print out the area
}
fn area(rectangle: Rectangle) -> u64 {
rectangle.width * rectangle.height
}
We defined a struct of type Rectangle
, with elements width
and height
. Now in the area
function, we can pass a variable of type Rectangle
and directly access its elements using dot notation to calculate the product.
This way of writing is clear and simple. First, we only need to pass a struct when passing parameters. Second, the struct type explicitly indicates that it is a rectangle. Finally, in the area
function, we can clearly know that its function is to calculate the area of the rectangle through the width
and height
of the rectangle.
Let's consider a problem. We put width
and height
in the Rectangle
struct. If we want to know their values, we need to print the Rectangle
struct:
use debug::PrintTrait;
struct Rectangle {
width: u64,
height: u64,
}
fn main() {
let rectangle = Rectangle { width: 30, height: 10, };
rectangle.print();
}
However, this code will report an error:
error: Method `print` not found on type "main::main::Rectangle". Did you import the correct trait and impl?
--> main.cairo:10:15
rectangle.print();
^***^
The error message suggests that the Rectangle
type does not implement the print
trait. We know that many types have implemented the print
trait, such as u256, u64, so they can use the .print()
calling method. But the Rectangle
type has not implemented the print
trait. We can manually implement the print
trait for the Rectangle
type:
use debug::PrintTrait;
struct Rectangle {
width: u64,
height: u64,
}
fn main() {
let rectangle = Rectangle { width: 30, height: 10, };
rectangle.print();
}
impl RectanglePrintImpl of PrintTrait<Rectangle> {
fn print(self: Rectangle) {
self.width.print();
self.height.print();
}
}
After implementing the print
trait, we can use rectangle.print()
to print the data of type Rectangle
.
Conclusion
In this article, we learned about the use cases of structs, how using structs can optimize code writing and readability, and how we can implement various traits for structs to accelerate program development.
Low latency and free Starknet node awaits!
For a limited time, Reddio is offering unrestricted access to its high-speed StarkNet Node, completely free of charge. This is an unparalleled opportunity to experience the fastest connection with the lowest delay. All you need to do is register an account on Reddio at https://dashboard.reddio.com/ and start exploring the limitless possibilities.
You can discover why Reddio claims the fastest connection by reading more here.