Closure는 함수와 같은 기능을 하지만 다른 점이 있다.
- argument를 소괄호( )가 아닌 | | 을 통해 받는다.
- 코드가 한 줄일 때 중괄호 { } 를 생략할 수 있다.
- 외부의 변수(variable)를 사용할 수 있다.
가장 큰 차이점은 3번이다. 다른 언어는 일반적인 함수에서 외부 변수를 사용할 수 있지만 Rust에서는 사용 할 수 없다. 따라서 Closure를 사용해야 한다.
Rust는 Pure function을 지향한다. 즉, 어떤 함수를 만들고 하나의 입력값을 넣어주면 결과값이 동일해야 한다. 그런데 함수에서 외부 변수를 가져다 사용할 때, 외부 변수가 mut
으로 선언돼서 값이 바뀐다면? 우리가 만든 함수에서 변경된 코드는 없는데 결과값이 달라지게 된다. 이러한 이유로 Rust의 함수는 외부 변수를 사용하지 못하게 하는 것이다.
Rust가 Pure function을 지향한다면, Closure에서도 외부 변수를 사용할 수 없게 해야하는 것 아닌가? 이게 내로남불 뭐 그런건가? 사실 Closure는 일반 함수와 다르게 일회용 함수 다.
한 번 쓰고 버릴 함수라면 외부 변수를 사용해도 상관 없다. 왜? 어차피 한 번만 사용할 거니까. 나중에 외부 변수의 값이 바뀌어도 특정 시점에 잠깐 쓰고 말지 뭐 그런 느낌.
core::option::Option
pub const fn unwrap_or_else<F>(self, f: F) -> T
where
F: FnOnce() -> T + Destruct,
unwrap_or_else()
함수는 Closure의 한 형태인 FnOnce
를 타입으로 받아서 T 타입을 리턴해주는 함수다. 아래 예시를 보면
let k = 10;
assert_eq!(Some(4).unwrap_or_else(|| 2 * k), 4);
assert_eq!(None.unwrap_or_else(|| 2 * k), 20);
Some value가 있을 때는 안에 있는 값을 unwrap 해서 리턴해주고, Some value가 없을 때는 인자로 받은 Closure를 실행하는데 예시에 있는 Closure는 외부 변수인 k를 인자로 받아서 2를 곱해준 값을 리턴한다.
예시코드를 보기 전에 필요한 것들을 정리해보자.
core::slice
pub const fn get<I>(&self, index: I) -> Option<&I::Output>
where
I: SliceIndex<Self>,
get()
은 Option을 리턴한다.
enum Option<T> {
None,
Some(T),
}
Option
은 Some이나 None을 리턴한다.
enum Result<T, E> {
Ok(T),
Err(E),
}
Result
는 OK 또는 에러를 리턴한다.
자 이제 필요한 것들은 다 끝났으니, 예제코드를 살펴보자.
fn main() {
let my_vec = vec![8, 9, 10];
let fourth = my_vec.get(3).unwrap_or_else(|| { // try to unwrap. If it doesn't work,
if my_vec.get(0).is_some() { // see if my_vec has something at index [0]
&my_vec[0] // Give the number at index 0 if there is something
} else {
&0 // otherwise give a &0
}
});
println!("{}", fourth);
}
unwrap_or_else()
는 get()
에서 리턴하는 None을 받기 때문에(vector의 3번째 항목은 값이 없음) 안에 있는 Closure를 실행한다. Closure는 vector에서 0번째 항목을 Some으로 리턴하고 0번째 vector 값을 ref 해서 리턴한다.