프로그래밍러스트 개발자

러스트에서 클로저(closures)는 어떻게 구성되어 있나요? 클로저의 유형은 무엇이 있으며, 이들은 무엇이 다른가요, 그리고 언제 사용되나요?

Hintsage AI 어시스턴트로 면접 통과

답변

러스트에서 클로저는 외부 가시성 영역에서 변수를 "캡처"할 수 있는 익명 함수입니다. 문법:

let add = |a: i32, b: i32| a + b;

러스트에는 세 가지 유형의 클로저가 있습니다(변수 캡처 방식에 따라 다름):

  • Fn: 참조(&T)를 캡처하며, 환경을 변경하지 않고 여러 번 호출할 수 있습니다.
  • FnMut: 변경 가능 참조(&mut T)를 캡처하며, 호출 시 클로저 내의 데이터를 변경할 수 있습니다.
  • FnOnce: 캡처된 데이터 소유권을 가져오기 때문에 한 번만 호출할 수 있습니다(T).

러스트는 필요한 유형을 자동으로 결정하지만, 명시적으로 지정할 수도 있습니다.

차이점 예:

let s = String::from("hello"); // FnOnce let consume = move || println!("{}", s); // 호출 후 s는 더 이상 접근할 수 없음

함정 질문

move 키워드를 가진 클로저가 FnOnce가 아니라 Fn 또는 FnMut가 될 수 있을까요?

답변: move 키워드는 변수를 소유권으로 이동시켜 캡처하지만, 클로저 내부의 데이터가 변경되지 않고 파괴되지 않는 경우, 클로저는 Fn/FnMut와 호환성을 유지합니다. 예:

let s = String::from("abc"); let c = move || println!("String: {}", s); // c: impl Fn() c();

c는 여러 번 호출할 수 있습니다: s의 값이 클로저에 복사되었지만 파괴되지는 않았습니다.

주제에 대한 세부 사항을 알지 못해 발생한 실제 오류 사례


이야기

데이터 스트림 프로젝트에서 초보 러스트 개발자는 move 없이 클로저를 작성하고 다른 스레드에 전달하려고 했습니다. 그 결과 컴파일러는 outlived borrow에 대해 경고하며, 라이프타임과 move의 세부 사항을 긴급히 이해해야 했습니다.


이야기

클로저 내부에서 가변성을 도입했지만, 유형을 FnMut로 변경하지 않아 iterator 사용 시 "mismatched types" 오류가 발생했습니다.


이야기

큰 배열을 처리할 때 클로저가 move로 전체 컬렉션을 가져가 소유권을 이동시켜, 바깥쪽 코드가 첫 번째 반복 이후 데이터에 접근할 수 없게 되면서 moved value에 접근하려고 시도하게 되었습니다.