Enums
Los Enums, abreviatura de "enumeraciones", son una forma de definir un tipo de datos personalizado que consiste en un conjunto fijo de valores nombrados, llamados variantes. Los enums son útiles para representar una colección de valores relacionados donde cada valor es distinto y tiene un significado específico.
Enum Variants and Values
Aquí hay un ejemplo sencillo de un enum:
#[derive(Drop)]
enum Direction {
North,
East,
South,
West,
}
In this example, we've defined an enum called Direction
with four variants: North
, East
, South
, and West
. The naming convention is to use PascalCase for enum variants. Each variant represents a distinct value of the Direction type. In this particular example, variants don't have any associated value. One variant can be instantiated using this syntax:
#[derive(Drop)] enum Direction { North, East, South, West, } fn main() { let direction = Direction::North; }
It's easy to write code that acts differently depending on the variant of an enum instance, in this example to run specific code according to a Direction. You can learn more about it on The Match Control Flow Construct page.
Enums Combined with Custom Types
Los enums también pueden ser utilizados para almacenar datos más interesantes asociados con cada variante. Por ejemplo:
#[derive(Drop)]
enum Message {
Quit,
Echo: felt252,
Move: (u128, u128),
}
En este ejemplo, el enum Message
tiene tres variantes: Quit
, Echo
y Move
, todas con tipos diferentes:
Quit
doesn't have any associated value.Echo
is a single felt.Move
is a tuple of two u128 values.
Incluso puedes usar una estructura o otro enum que hayas definido dentro de una de las variantes de tu enum.
Trait Implementations for Enums
En Cairo, puedes definir traits e implementarlos para tus enums personalizados. Esto te permite definir métodos y comportamientos asociados con el enum. Aquí hay un ejemplo de cómo definir un trait e implementarlo para el enum Message
anterior:
trait Processing {
fn process(self: Message);
}
impl ProcessingImpl of Processing {
fn process(self: Message) {
match self {
Message::Quit => { 'quitting'.print(); },
Message::Echo(value) => { value.print(); },
Message::Move((x, y)) => { 'moving'.print(); },
}
}
}
En este ejemplo, implementamos el trait Processing
para Message
. Así es cómo podría ser utilizado para procesar un mensaje Quit:
use debug::PrintTrait; #[derive(Drop)] enum Message { Quit, Echo: felt252, Move: (u128, u128), } trait Processing { fn process(self: Message); } impl ProcessingImpl of Processing { fn process(self: Message) { match self { Message::Quit => { 'quitting'.print(); }, Message::Echo(value) => { value.print(); }, Message::Move((x, y)) => { 'moving'.print(); }, } } } fn main() { let msg: Message = Message::Quit; msg.process(); }
Al ejecutar este código se imprimiría quitting
.
The Option Enum and Its Advantages
El enum Option es un enum estándar en Cairo que representa el concepto de un valor opcional. Tiene dos variantes: Some: T
y None: ()
. Some: T
indica que hay un valor de tipo T
, mientras que None
representa la ausencia de un valor.
enum Option<T> {
Some: T,
None: (),
}
El enum Option
es útil porque te permite representar explícitamente la posibilidad de que un valor esté ausente, lo que hace que tu código sea más expresivo y fácil de entender. Usar Option
también puede ayudar a prevenir errores causados por el uso de valores null
no inicializados o inesperados.
Para darte un ejemplo, aquí hay una función que devuelve el índice del primer elemento de un arreglo con un valor dado, o None
si el elemento no está presente.
Estamos demostrando dos enfoques para la función anterior:
- Recursive Approach
find_value_recursive
- Iterative Approach
find_value_iterative
Note: in the future it would be nice to replace this example by something simpler using a loop and without gas related code.
fn find_value_recursive(arr: @Array<felt252>, value: felt252, index: usize) -> Option<usize> {
if index >= arr.len() {
return Option::None;
}
if *arr.at(index) == value {
return Option::Some(index);
}
find_value_recursive(arr, value, index + 1)
}
fn find_value_iterative(arr: @Array<felt252>, value: felt252) -> Option<usize> {
let length = arr.len();
let mut index = 0;
let mut found: Option<usize> = Option::None;
loop {
if index < length {
if *arr.at(index) == value {
found = Option::Some(index);
break;
}
} else {
break;
}
index += 1;
};
return found;
}
#[cfg(test)]
mod tests {
use debug::PrintTrait;
use super::{find_value_recursive, find_value_iterative};
#[test]
#[available_gas(999999)]
fn test_increase_amount() {
let mut my_array = ArrayTrait::new();
my_array.append(3);
my_array.append(7);
my_array.append(2);
my_array.append(5);
let value_to_find = 7;
let result = find_value_recursive(@my_array, value_to_find, 0);
let result_i = find_value_iterative(@my_array, value_to_find);
match result {
Option::Some(index) => { if index == 1 {
'it worked'.print();
} },
Option::None => { 'not found'.print(); },
}
match result_i {
Option::Some(index) => { if index == 1 {
'it worked'.print();
} },
Option::None => { 'not found'.print(); },
}
}
}
Al ejecutar este código se imprimiría it worked
.