# Rust - Option & Result

eslerkang·2022년 3월 17일
0

목록 보기
10/10

## Option

Option is concept from OCaml language. And it's used for some action can have no result or some result. As Rust don't have null, we can use None of Option

Option is looks like

enum Option<T> {
None,
Some(T)
}

It means, if there is no result, it's None. If there is some result of type T, it's Some(T).

We can imagine a situation that we want to get 5th value of a Vector.

fn take_fifth(value: Vec<i32>) -> Option<i32> {
if value.len() < 5 {
None // Option::None
} else {
Some(value[4]) // Some(i32)
}
}

fn main() {
let vec = vec![1, 2];
let fifth = take_fifth(vec);
println!("{:?}", fifth); // None

let vec = vec![1, 2, 3, 4, 5, 6];
let fifth = take_fifth(vec);
println!("{:?}", fifth); // Some(5)
}

take_fifth function check the length of Vector is less than 5 and if it is, returns None. If not, return Some(value[4]) that means fifth value of the Vector. The result is like the comment right after println statement.
Now, we got None or Some(5). But as we can see, 5 is wrapped in Option::Some.
So if you want to treat 5 as an i32 value, we have to unwrap it.

fn take_fifth(value: Vec<i32>) -> Option<i32> {
if value.len() < 5 {
None // Option::None
} else {
Some(value[4]) // Some(i32)
}
}

fn main() {
let vec = vec![1, 2];
let fifth = take_fifth(vec);
println!("{:?}", fifth); // None

let vec = vec![1, 2, 3, 4, 5, 6];
let fifth = take_fifth(vec);
println!("{:?}", fifth); // Some(5)

let i32_fifth = fifth.unwrap();
println!("{}", i32_fifth);
}

We can use .unwrap() method to unwrap and pull out the real value. But, we can imagine what if we unwrap None. If you try to unwrap None, you can see a panic thread 'main' panicked at 'called 'Option::unwrap()' on a 'None' value'.
This means as None doesn't have value, you cannot unwrap None.

So how could we check if an Option is None or Some? We can use match statement or .is_some(), .is_none() or .expect()

First, we can use match statement.

fn take_fifth(value: Vec<i32>) -> Option<i32> {
if value.len() < 5 {
None // Option::None
} else {
Some(value[4]) // Some(i32)
}
}

fn main() {
let vec = vec![1, 2];
let fifth = take_fifth(vec);
println!("{:?}", fifth); // None

match fifth {
Some(number) => println!("I got {}", number),
None => println!("None..")
} // None..

let vec = vec![1, 2, 3, 4, 5, 6];
let fifth = take_fifth(vec);
println!("{:?}", fifth); // Some(5)

match fifth {
Some(number) => println!("I got {}", number),
None => println!("None..")
} // I got 5
}

As we can see, we used match fifth{}. We can check if fifth is Some(number) or None and printed the number or "None..". So we can check and printed right result using match statement.

Second, we can check if Option is None or Some(T) by .is_none() or .is_some().

fn take_fifth(value: Vec<i32>) -> Option<i32> {
if value.len() < 5 {
None // Option::None
} else {
Some(value[4]) // Some(i32)
}
}

fn main() {
let vec = vec![1, 2];
let fifth = take_fifth(vec);
println!("{:?}", fifth); // None

if fifth.is_none() {
println!("I got Nothing");
} // I got Nothing

let vec = vec![1, 2, 3, 4, 5, 6];
let fifth = take_fifth(vec);
println!("{:?}", fifth); // Some(5)

if fifth.is_some() {
println!("I got {}", fifth.unwrap());
} // I got 5
}

Code above, we used .is_none() and .is_some() to check if its None or Some. We can know the meaning of the code intuitively.

At last, we can use .expect() to deal with None and Some.

fn take_fifth(value: Vec<i32>) -> Option<i32> {
if value.len() < 5 {
None // Option::None
} else {
Some(value[4]) // Some(i32)
}
}

fn main() {
let vec = vec![1, 2];
let fifth = take_fifth(vec);
println!("{:?}", fifth); // None

// println!("We expect: {}", fifth.expect("Needed at least five items - make sure Vec has at least 5 items"));
// panic occur

let vec = vec![1, 2, 3, 4, 5, 6];
let fifth = take_fifth(vec);
println!("{:?}", fifth); // Some(5)

println!("We expect: {}", fifth.expect("Needed at least five items - make sure Vec has at least 5 items"));
// We expect: 5
}

Code above, we used fifth.expect("~~~"). And ~~~ is the panic message if there is a panic. If we use .expect, Rust check if our Option is Some and if its None, make a panic with the message we gave. And if its Some, .expect returns the value inside.

Like I wrote above, we've made Option, check and unwrapped Option with various ways. We can use Option for showing null in Rust.

## Result

As Option is maybe there, maybe not, Result is it can work or not.
Result is looks like

enum Result<T, E> {
Ok(T),
Err(E)
}

T is generics for result, and E is generics for errors. So if some action can executed, it will have Ok, but if it cannot executed, it will have Err.

Let's see a simple example. We can imagine if we want to get error if input is odd, and get ok if input is even.

fn check_even(input: i32) -> Result<(), ()> {
if input % 2 == 0 {
Ok(())
} else {
Err(())
}
}

fn main() {
match check_even(34) {
Ok(_) => println!("Okay!!"),
Err(_) => println!("Error!!");
}
// Okay!!
}

Everything is almost same as Option. But, Result gets two generics. The first () is for Ok, and second () is for Err(() is unit type that means nothing). So Ok and Err don't have any value.
And as we wrote like Some(number) => println!("I got {}", number), at the match statement of Option, we can use like Ok(x) => ~~ in match statement.

Same as Option, the result of Result is wrapped in Result type. So if you want to get original value of Result, we have to use .unwrap() here too. But as we met Panic when we unwrapped None, we'll meet Panic if we unwrap Err. We have to check if its Err or Ok.

We can use match statement, .is_ok(), .is_err(), or .expect() as what we did at Option. I'll omit the examples cause they'll almost same as Option's examples.

So you can choose between Option and Result by situation. And the usage is almost the same. Option is for no result or result, Result is for error or normal result.

Surfer surfing on the dynamic flow of the world