Capturing
클로져는 타입을 지정하고 선언하는 데 있어 상당히 자유롭게 작용하며 대부분 컴파일러가 알아서 파악한다. 즉 캡쳐링 함에 있어 해당 변수를 borrow 할지 mut borrow 할 지, move 할 지 클로져 함수에 따라 컴파일러가 알아서 알아낸다.
- borrow &T 참조만 하는 경우.
- mut borrow &mut T 변환가능하게 참조하는 경우.
- T 데이터를 아예 가져오는 경우.
크게는 이 세가지로 분류하며 이 경우를 잘 파악하고 인지하고 있어야 러스트를 깊이 있게 이해할 수 있다.
예제를 통해 알아보자.
fn main() { let color = String::from("red"); // 캡쳐해서 변수의 값을 가져오는데, 이때는 & 참조로만 사용한다. let print = || println!("참조만 하기 -> {color}"); // print() 클로져에서 color 를 참조로만 사용했기 때문에 다른 변수에 참조로 값을 선언할 수 있다. let t = &color; print(); // 하지만 color 를 그대로 참조없이 다른 변수에 넣어버리면 더 이상 print() 클로져를 사용할 수 // 없게 된다. 즉 아래 변수 선언은 컴파일 에러가 난다. // let move_color = color; print(); let mut count = 0; // inc() 클로져의 경우 count 를 캡쳐 한 후 숫자 1 씩 추가하는 함수인데, 이렇게 하기 위해선 // 단순히 borrow 가 아닌 &mut 또는 아예 가져와야하는데, 이럴 때에는 &mut 를 컴파일러가 알아서 // 사용한다. let mut inc = || { count += 1; println!("&mut 변환가능한 참조 하기 -> increase count by 1, current number is : {count}"); }; inc(); // inc() 클로져에서 이미 count 변수를 &mut 하게 사용하기 때문에 다른 변수에 선언할 수 없다. //let _count_borrowed = &mut count; inc(); inc(); //카피가 되지 않는 타입으로 선언. 힙에 저장된 변수. let movable = Box::new(3); //drop 함수는 std::lib 에 존재하는 함수인데, 데이터 객체 그 자체를 필요로 하기 때문에 아예 //move한다. let consume = || { println!("객체를 아예 옮겨버리기 -> movable : {:?}", movable); drop(movable); }; consume(); // 그렇기 때문에 아래 변수는 사용될 수 없다. 이미 consume 클로져에서 선언되었고 move했기 // 때문이다. 모순 발생. //println!("{:?}", movable); let haystack = vec![1, 2, 3]; //move 키워드를 사용하면 캡쳐링한 변수는 클로져로 이동하게 되어 사용할 수 없다. let contains = move |needle| haystack.contains(needle); println!("{}", contains(&3)); //아래 haystack 을 프린트 하는 함수를 사용하면 컴파일 에러가 난다. //println!("{:?}", haystack); println!("{}", contains(&4)); }