An Example Program Using Structs
Para entender cuándo podríamos usar estructuras, escribamos un programa que calcule el área de un rectángulo. Comenzaremos usando variables individuales y luego reescribiremos el programa hasta que estemos usando estructuras en su lugar.
Let’s make a new project with Scarb called rectangles that will take the width and height of a rectangle specified in pixels and calculate the area of the rectangle. Listing 5-6 shows a short program with one way of doing exactly that in our project’s src/lib.cairo.
Filename: src/lib.cairo
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 }
Now run the program with scarb cairo-run
:
$ scarb cairo-run
[DEBUG] , (raw: 300)
Run completed successfully, returning []
Este código logra calcular el área del rectángulo llamando a la función area
con cada dimensión, pero podemos hacer más para que este código sea claro y legible.
El problema con este código es evidente en la declaración de la función area
:
fn area(width: u64, height: u64) -> u64 {
The area
function is supposed to calculate the area of one rectangle, but the function we wrote has two parameters, and it’s not clear anywhere in our program that the parameters are related. It would be more readable and more manageable to group width and height together. We’ve already discussed one way we might do that in Chapter 2: using tuples.
Refactoring with Tuples
Listing 5-7 shows another version of our program that uses tuples.
Filename: src/lib.cairo
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 }
En cierto modo, este programa es mejor. Las tuplas nos permiten agregar un poco de estructura y ahora estamos pasando solo un argumento. Pero en otro sentido, esta versión es menos clara: las tuplas no nombran sus elementos, por lo que tenemos que indexar las partes de la tupla, lo que hace que nuestro cálculo sea menos obvio.
Mezclar el ancho y la altura no importaría para el cálculo del área, pero si queremos calcular la diferencia, ¡sería importante! Tendríamos que tener en cuenta que width
es el índice de tupla 0
y height
es el índice de tupla 1
. Esto sería aún más difícil de entender y tener en cuenta para otra persona si usara nuestro código. Debido a que no hemos transmitido el significado de nuestros datos en nuestro código, ahora es más fácil introducir errores.
Refactoring with Structs: Adding More Meaning
Usamos estructuras para agregar significado al etiquetar los datos. Podemos transformar la tupla que estamos usando en una estructura con un nombre para el todo y nombres para las partes.
Filename: src/lib.cairo
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 }
Aquí hemos definido una estructura y la hemos llamado Rectangle
. Dentro de las llaves, definimos los campos como width
y height
, los cuales tienen el tipo u64
. Luego, en main
, creamos una instancia particular de Rectangle
que tiene un ancho de 30
y una altura de 10
. Nuestra función area
ahora está definida con un parámetro, al que hemos llamado rectangle
que es de tipo de la estructura Rectangle
. Luego podemos acceder a los campos de la instancia con notación de punto, y dar nombres descriptivos a los valores en lugar de usar los valores de índice de tupla de 0
y 1
.
Adding Useful Functionality with Trait
It’d be useful to be able to print an instance of Rectangle
while we’re debugging our program and see the values for all its fields. Listing 5-9 tries using the print
as we have used in previous chapters. This won’t work.
Filename: src/lib.cairo
use debug::PrintTrait; struct Rectangle { width: u64, height: u64, } fn main() { let rectangle = Rectangle { width: 30, height: 10, }; rectangle.print(); }
Cuando compilamos este código, obtenemos un error con el siguiente mensaje:
$ cairo-compile src/lib.cairo
error: Method `print` not found on type "../src::Rectangle". Did you import the correct trait and impl?
--> lib.cairo:16:15
rectangle.print();
^***^
Error: Compilation failed.
The print
trait is implemented for many data types, but not for the Rectangle
struct. We can fix this by implementing the PrintTrait
trait on Rectangle
as shown in Listing 5-10.
To learn more about traits, see Traits in Cairo.
Filename: src/lib.cairo
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(); } }
¡Bien! No es el resultado más bonito, pero muestra los valores de todos los campos para esta instancia, lo que definitivamente ayudaría durante la depuración.