[Rust] 함수의 타입: fn, Fn, FnMut, FnOnce
러스트에서 함수의 타입을 나타내는 방법은 크게 함수포인터 타입과, 트레잇을 통해 나타내는 방법이 있다.
그에 대해 대충 정리해본다.
함수 포인터 타입
단순한 함수들은 함수포인터 타입으로 나타낼수 있다.
그리 대단한건 아니고, 그냥 fn() -> () 의 형태로 적는 것이다.


일단은 잘 돌아가는 걸 볼 수가 있다.
일반 함수들을 넣어도 다 잘 돌 것이다.
근데 이 포인터타입 기능에는 꽤 큰 맹점이 있다.
다음과 같이 클로저가 캡쳐를 갖도록 하면 어떻게 될까?
이전 테스트에서는 캡쳐를 한게 없어서 그냥 함수와 다를게 없었다.


그럼 이제 클로저가 들어갔다고 에러를 던진다.

원래 클로저는 안되는게 맞는데, 그냥 캡쳐가 없으면 봐주는 형태였던 것이다.
이것뿐만 아니라 함수포인터 방식은 임의로 만들어진 함수형 객체에도 대응을 할 수 없다는 단점이 존재한다.
그래서 이런 처리는 좀 단순한 경우에만 쓸만하고, 대체로는 트레잇을 사용하는걸 권장한다.
트레잇: Fn, FnMut, FnOnce
Fn 등은 함수를 나타내는 특수한 트레잇이다.
모든 함수와 클로저는 저것들을 자동으로 구현한다.
Fn은 불변 참조 캡쳐만 가지는 클로저와 모든 함수포인터에 구현되고.
FnMut는 가변 참조 캡처를 가지는 클로저, 함수포인터에 구현된다.
FnOnce는 저 둘의 상위 트레잇이다. 모든 클로저와 함수포인터에 구현된다.
이 트레잇들을 응용하면 직접 함수객체를 구현해서 사용할 수도 있다. 현재는 nightly에 한해서 가능하다.

근데 이렇게 할일은 딱히 없을것같고
다시 돌아가서, 이 트레잇들을 사용하면 클로저에 대한 처리가 가능해진다.
트레잇을 직접 타입에 때려박는건 번거롭고 불편하니, 보통 제너릭을 통해 정적 디스패치를 유도하는 식으로 쓴다. impl 써도 되고.



만약 클로저에서 밖에 있는 값을 캡쳐해다 수정한다면, 그 상황에서 클로저를 받으려면 Fn 대신 FnMut를 사용해야만 한다.

클로저에서 뭔가 값 변경을 시도하면 Fn이 구현되지 않기 때문이다.
Fn와 FnMut이 위처럼 상태 제어에 따라 왔다갔다 구현되는 반면, FnOnce는 항상 모든 클로저에 구현된다.


FnOnce는 대신 한번만 호출될 수 있다는 제약이 있다. 이 트레잇의 호출은 (self)로 적용되어있기 때문에, 호출시 이동이 되어서 죽어버린다.


그렇다.
참조
https://stackoverflow.com/questions/54181816/function-pointer-vs-fn-trait-object
https://stackoverflow.com/questions/38672235/how-do-i-implement-the-fn-trait-for-one-struct-for-different-types-of-arguments
https://stackoverflow.com/questions/30177395/when-does-a-closure-implement-fn-fnmut-and-fnonce