Kurtarılabilir Hatalar ile Result
Çoğu hata, programın tamamen durmasını gerektirecek kadar ciddi değildir. Bazen, bir fonksiyon başarısız olduğunda, kolayca yorumlayıp yanıt verebileceğiniz bir nedenle başarısız olur. Örneğin, iki büyük tamsayı eklemeye çalıştığınızda ve işlem, toplam maksimum temsil edilebilir değeri aştığı için taşarsa, tanımsız davranışa neden olmak veya işlemi sonlandırmak yerine bir hata veya sarılı bir sonuç döndürmek isteyebilirsiniz.
The Result
Enum
Recall from Generic data types section in Chapter 8 that the Result
enum is defined as having two variants, Ok
and Err
, as follows:
enum Result<T, E> {
Ok: T,
Err: E,
}
Result<T, E>
enumu iki genel tür, T
ve E
içerir ve iki varyanta sahiptir: Ok
, T
türünde bir değer tutar ve Err
, E
türünde bir değer tutar. Bu tanım, bir işlemin başarılı olabileceği (bir T
türünde değer döndürerek) veya başarısız olabileceği (bir E
türünde değer döndürerek) herhangi bir yerde Result
enumunu kullanmayı uygun hale getirir.
ResultTrait
ResultTrait
özelliği, Result<T, E>
enumu ile çalışmak için yöntemler sağlar, örneğin değerleri açma, Result
'un Ok
veya Err
olup olmadığını kontrol etme ve özel bir mesajla panik yapma. ResultTraitImpl
uygulaması, bu yöntemlerin mantığını tanımlar.
trait ResultTrait<T, E> {
fn expect<+Drop<E>>(self: Result<T, E>, err: felt252) -> T;
fn unwrap<+Drop<E>>(self: Result<T, E>) -> T;
fn expect_err<+Drop<T>>(self: Result<T, E>, err: felt252) -> E;
fn unwrap_err<+Drop<T>>(self: Result<T, E>) -> E;
fn is_ok(self: @Result<T, E>) -> bool;
fn is_err(self: @Result<T, E>) -> bool;
}
expect
ve unwrap
yöntemleri, her ikisi de Result<T, E>
'yi Ok
varyantında olduğunda T
tipindeki değeri çıkarmaya çalıştıkları için benzerdir. Result
Ok(x)
ise, her iki yöntem de değer x
'i döndürür. Ancak, iki yöntem arasındaki temel fark, Result
Err
varyantında olduğunda gösterilen davranıştadır. expect
yöntemi, panik yapılırken kullanılacak özel bir hata mesajı (felt252 değeri olarak) sağlamanıza izin verir, bu da panik üzerinde daha fazla kontrol ve bağlam sağlar. Öte yandan, unwrap
yöntemi, varsayılan bir hata mesajı ile panik yapar ve panik nedeni hakkında daha az bilgi sağlar.
The expect_err
and unwrap_err
methods have the exact opposite behavior. If the Result
is Err(x)
, both methods return the value x
. However, the key difference between the two methods is in case of Result::Ok()
. The expect_err
method allows you to provide a custom error message (as a felt252
value) that will be used when panicking, giving you more control and context over the panic. On the other hand, the unwrap_err
method panics with a default error message, providing less information about the cause of the panic.
A careful reader may have noticed the <+Drop<T>>
and <+Drop<E>>
in the first four methods signatures. This syntax represents generic type constraints in the Cairo language, as seen in the previous chapter. These constraints indicate that the associated functions require an implementation of the Drop
trait for the generic types T
and E
, respectively.
Son olarak, is_ok
ve is_err
metodları, bir Result
enum değerinin varyantını kontrol etmek için ResultTrait
özelliği tarafından sağlanan yardımcı fonksiyonlardır.
is_ok
, birResult<T, E>
değerinin anlık görüntüsünü alır veResult
Ok
varyantıysa, yani işlem başarılı olduysatrue
döner.Result
Err
varyantıysa,false
döner.is_err
takes a snapshot of aResult<T, E>
value and returnstrue
if theResult
is theErr
variant, meaning the operation encountered an error. If theResult
is theOk
variant, it returnsfalse
.
These methods are helpful when you want to check the success or failure of an operation without consuming the Result
value, allowing you to perform additional operations or make decisions based on the variant without unwrapping it.
ResultTrait
uygulamasını burada bulabilirsiniz.
It is always easier to understand with examples. Have a look at this function signature:
fn u128_overflowing_add(a: u128, b: u128) -> Result<u128, u128>;
It takes two u128
integers, a
and b
, and returns a Result<u128, u128>
where the Ok
variant holds the sum if the addition does not overflow, and the Err
variant holds the overflowed value if the addition does overflow.
Şimdi, bu fonksiyonu başka bir yerde kullanabiliriz. Örneğin:
fn u128_checked_add(a: u128, b: u128) -> Option<u128> {
match u128_overflowing_add(a, b) {
Result::Ok(r) => Option::Some(r),
Result::Err(r) => Option::None,
}
}
Here, it accepts two u128
integers, a
and b
, and returns an Option<u128>
. It uses the Result
returned by u128_overflowing_add
to determine the success or failure of the addition operation. The match
expression checks the Result
from u128_overflowing_add
. If the result is Ok(r)
, it returns Option::Some(r)
containing the sum. If the result is Err(r)
, it returns Option::None
to indicate that the operation has failed due to overflow. The function does not panic in case of an overflow.
Let's take another example:
fn parse_u8(s: felt252) -> Result<u8, felt252> {
match s.try_into() {
Option::Some(value) => Result::Ok(value),
Option::None => Result::Err('Invalid integer'),
}
}
In this example, the parse_u8
function takes a felt252
and tries to convert it into a u8
integer using the try_into
method. If successful, it returns Result::Ok(value)
, otherwise it returns Result::Err('Invalid integer')
.
İki test vakamız şunlardır:
fn parse_u8(s: felt252) -> Result<u8, felt252> {
match s.try_into() {
Option::Some(value) => Result::Ok(value),
Option::None => Result::Err('Invalid integer'),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_felt252_to_u8() {
let number: felt252 = 5;
// should not panic
let res = parse_u8(number).unwrap();
}
#[test]
#[should_panic]
fn test_felt252_to_u8_panic() {
let number: felt252 = 256;
// should panic
let res = parse_u8(number).unwrap();
}
}
Don't worry about the #[cfg(test)]
attribute for now. We'll explain in more detail its meaning in the next Testing Cairo Programs chapter.
#[test]
attribute means the function is a test function, and #[should_panic]
attribute means this test will pass if the test execution panics.
The first one tests a valid conversion from felt252
to u8
, expecting the unwrap
method not to panic. The second test function attempts to convert a value that is out of the u8
range, expecting the unwrap
method to panic with the error message Invalid integer
.
The ?
Operator
Son olarak bahsedeceğimiz operatör ?
operatörüdür. ?
operatörü, daha özdeş ve özlü hata işleme için kullanılır. Bir Result
veya Option
türü üzerinde ?
operatörünü kullandığınızda, aşağıdakileri yapacaktır:
- Değer
Result::Ok(x)
veyaOption::Some(x)
ise, iç değerix
'i doğrudan döndürür. - Değer
Result::Err(e)
veyaOption::None
ise, hatayı veyaNone
'ı hemen fonksiyondan dönerek yayılır.
?
operatörü, hataları dolaylı olarak ele almak ve çağıran fonksiyonun bunlarla ilgilenmesine izin vermek istediğinizde kullanışlıdır.
İşte bir örnek
fn do_something_with_parse_u8(input: felt252) -> Result<u8, felt252> {
let input_to_u8: u8 = parse_u8(input)?;
// DO SOMETHING
let res = input_to_u8 - 1;
Result::Ok(res)
}
We can see that do_something_with_parse_u8
function takes a felt252
value as input and calls parse_u8
function. The ?
operator is used to propagate the error, if any, or unwrap the successful value.
Ve küçük bir test vakası ile:
fn parse_u8(s: felt252) -> Result<u8, felt252> {
match s.try_into() {
Option::Some(value) => Result::Ok(value),
Option::None => Result::Err('Invalid integer'),
}
}
fn do_something_with_parse_u8(input: felt252) -> Result<u8, felt252> {
let input_to_u8: u8 = parse_u8(input)?;
// DO SOMETHING
let res = input_to_u8 - 1;
Result::Ok(res)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_function_2() {
let number: felt252 = 258;
match do_something_with_parse_u8(number) {
Result::Ok(value) => println!("Result: {}", value),
Result::Err(e) => println!("Error: {}", e),
}
}
}
The console will print the error Invalid Integer
.
Özet
We saw that recoverable errors can be handled in Cairo using the Result
enum, which has two variants: Ok
and Err
. The Result<T, E>
enum is generic, with types T
and E
representing the successful and error values, respectively. The ResultTrait
provides methods for working with Result<T, E>
, such as unwrapping values, checking if the result is Ok
or Err
, and panicking with custom messages.
Kurtarılabilir hataları ele almak için, bir fonksiyon Result
türünde bir değer döndürebilir ve bir işlemin başarısı veya başarısızlığını ele almak için desen eşleştirme kullanabilir. ?
operatörü, hatayı dolaylı olarak ele almak veya başarılı değeri açmak için kullanılabilir. Bu, daha özlü ve net hata işleme sağlar, burada çağıran, çağrılan fonksiyon tarafından yükseltilen hataları yönetmekten sorumludur.