『TRPL Ch 13. Functional Language Features: Iterators and Closures』
- Closures: 변수에 저장할 수 있는 function-like 구조.
- Iterators: 배열의 요소를 처리할 수 있는 방법.
Closures: Anonymous Functions that Can Capture Their Environment
- 다른 프로그래밍 언어에서 말하는 클로저와 동일한 개념.
- 클로저를 정의하고 변수에 바인딩할 수 있다.
fn main() {
let closure = |num| {
println!("calculating slowly...");
thread::sleep(Duration::from_secs(2));
num
}
closure(3);
}
- 인자 타입을 명시할 수도 있고, 중괄호를 생략할수도 있다.
fn add_one_v1 (x: u32) -> u32 { x + 1 }
let add_one_v2 = |x: u32| -> u32 { x + 1 };
let add_one_v3 = |x| { x + 1 };
let add_one_v4 = |x| x + 1 ;
- 클로저와 클로저를 호출한 결과값을 갖는 구조체를 만들 수 있다.
- 구조체는 결과값을 필요할 때만 클로저를 호출하고, 결과값은 저장되어 다시 계산되지 않을 것이다. 이러한 패턴을 메모이제이션(memoization) 혹은 지연평가(lazy evaluation)이라고 한다.
Fn
트레잇을 사용해 타입으로 클로저를 취한다는 것을 명시할 수 있다. 모든 클로저는 Fn
, FnMut
, FnOnce
중 하나의 트레잇을 구현한다.
struct Cacher<T>
where T: Fn(u32) -> u32
{
calculation: T,
value: Option<u32>,
}
impl<T> Cacher<T>
where T: Fn(u32) -> u32
{
fn new(calculation: T) -> Cacher<T> {
Cacher {
calculation,
value: None,
}
}
fn value(&mut self, arg: u32) -> u32 {
match self.value {
Some(v) => v,
None => {
let v = (self.calculation)(arg);
self.value = Some(v);
v
},
}
}
}
fn main() {
let mut c = Cacher::new(|a| a);
let v1 = c.value(1);
let v2 = c.value(2);
assert_eq!(v2, 2);
}
- 클로저는 함수와 달리 환경을 캡처할 수 있다.
fn main() {
let x = 4;
let equal_to_x = |z| z == x;
let y = 4;
assert!(equal_to_x(y));
}
Fn
- 환경으로부터 값을 불변으로 빌린다.
FnMut
- 환경으로부터 값을 가변으로 빌린다. (환경을 변경할 수 있다.)
FnOnce
- 환경에서 캡처한 변수를 소비한다. 소비하기 위해 클로저가 변수의 오너십을 가져간다.
move
키워드를 이용해 클로저에게 강제로 오너십을 넘길 수도 있다.
fn main() {
let x = vec![1, 2, 3];
let equal_to_x = move |z| z == x;
println!("can't use x here: {:?}", x);
let y = vec![1, 2, 3];
assert!(equal_to_x(y));
}
Processing a Series of Items with Iterators
- 이터레이터를 게으르다. 이터레이터를 소비하는 메서드를 호출하기 전까지 이터레이터는 실행되지 않는다.
iter
메서드로 이터레이터를 만들 수 있다.
let v1 = vec![1, 2, 3];
let v1_iter = v1.iter();
- 이터레이터로부터 새로운 이터레이터를 만들기 위해 어댑터 메서드를 사용할 수 있다.
- 어댑터 메서드를 체이닝하는 것만으로는 이터레이터가 소비되지 않는다. 이터레이터가 게으르기 때문.
- 따라서
collect
메서드를 사용한다.
let v1: Vec<i32> = vec![1, 2, 3];
let v2: Vec<_> = v1.iter().map(|x| x + 1).collect();
assert_eq!(v2, vec![2, 3, 4]);
Iterator
트레잇을 구현하면 직접 이터레이터를 만들 수도 있다.
이 문서를 인용한 문서