Enumlar
Enumlar, "enumerasyonlar" için kısa, sabit bir adlandırılmış değer kümesinden oluşan özel bir veri tipi tanımlamanın bir yoludur. Enumlar, her biri belirgin ve belirli bir anlama sahip olan ilişkili değerlerin bir koleksiyonunu temsil etmek için yararlıdır.
Enum Varyantları ve Değerleri
İşte basit bir enum örneği:
#[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;
}
Now let's imagine that our variants have associated values, that store the exact degree of the direction. We can define a new Direction
enum:
#[derive(Drop)]
enum Direction {
North: u128,
East: u128,
South: u128,
West: u128,
}
fn main() {
let direction = Direction::North(10);
}
and instantiate it as follows:
#[derive(Drop)]
enum Direction {
North: u128,
East: u128,
South: u128,
West: u128,
}
fn main() {
let direction = Direction::North(10);
}
In this code, each variant is associated with a u128
value, representing the direction in degrees. In the next example, we will see that it is also possible to associate different data types with each variant.
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 in the Match Control Flow Construct section.
Enumlar Özel Tiplerle Birleştirilmiş
Enums can also be used to store more interesting custom data associated with each variant. For example:
#[derive(Drop)]
enum Message {
Quit,
Echo: felt252,
Move: (u128, u128),
}
In this example, the Message
enum has three variants: Quit
, Echo
, and Move
, all with different types:
Quit
herhangi bir ilişkilendirilmiş değere sahip değildir.Echo
is a singlefelt252
.Move
is a tuple of twou128
values.
You could even use a Struct or another enum you defined inside one of your enum variants.
Enumlar için Trait Uygulamaları
Cairo'da, özel enumlarınız için trait'leri tanımlayabilir ve bunları uygulayabilirsiniz. Bu, enumunuzla ilişkilendirilmiş metodları ve davranışları tanımlamanıza olanak tanır. İşte önceki Message
enumu için bir trait tanımlayıp bunu uygulama örneği:
trait Processing {
fn process(self: Message);
}
impl ProcessingImpl of Processing {
fn process(self: Message) {
match self {
Message::Quit => { println!("quitting") },
Message::Echo(value) => { println!("echoing {}", value) },
Message::Move((x, y)) => { println!("moving from {} to {}", x, y) },
}
}
}
In this example, we implemented the Processing
trait for Message
. Here is how it could be used to process a Quit
message:
#[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 => { println!("quitting") },
Message::Echo(value) => { println!("echoing {}", value) },
Message::Move((x, y)) => { println!("moving from {} to {}", x, y) },
}
}
}
fn main() {
let msg: Message = Message::Quit;
msg.process(); // prints "quitting"
}
The Option
Enum and Its Advantages
The Option
enum is a standard Cairo enum that represents the concept of an optional value. It has two variants: Some: T
and None
. Some: T
indicates that there's a value of type T
, while None
represents the absence of a value.
enum Option<T> {
Some: T,
None,
}
Option
enumu yararlıdır çünkü bir değerin yokluğunun açıkça temsil edilmesine izin verir, kodunuzu daha ifade edilebilir ve hakkında düşünmesi daha kolay hale getirir. Option
kullanmak, başlatılmamış veya beklenmedik null
değerleri nedeniyle oluşan hataları önlemeye de yardımcı olabilir.
To give you an example, here is a function which returns the index of the first element of an array with a given value, or None
if the element is not present.
Yukarıdaki fonksiyon için iki yaklaşımı gösteriyoruz:
- Recursive approach with
find_value_recursive
. - Iterative approach with
find_value_iterative
.
fn find_value_recursive(mut arr: Span<felt252>, value: felt252, index: usize) -> Option<usize> {
match arr.pop_front() {
Option::Some(index_value) => { if (*index_value == value) {
return Option::Some(index);
} },
Option::None => { return Option::None; },
};
find_value_recursive(arr, value, index + 1)
}
fn find_value_iterative(mut arr: Span<felt252>, value: felt252) -> Option<usize> {
let mut result = Option::None;
let mut index = 0;
while let Option::Some(array_value) = arr.pop_front() {
if (*array_value == value) {
result = Option::Some(index);
break;
};
index += 1;
};
result
}
Enums can be useful in many situations, especially when using the match
flow construct that we just used. We will describe it in the next section.
Other enums are used very often, such as the Result
enum, allowing to handle errors gracefully. We will explain the Result
enum in detail in the "Error Handling" chapter.