Rust 함수를 매개 변수로 어떻게 전달합니까?


89

함수를 매개 변수로 전달할 수 있습니까? 그렇지 않다면 좋은 대안은 무엇입니까?

몇 가지 다른 구문을 시도했지만 올바른 구문을 찾지 못했습니다. 나는 이것을 할 수 있다는 것을 안다.

fn example() {
    let fun: fn(value: i32) -> i32;
    fun = fun_test;
    fun(5i32);
}

fn fun_test(value: i32) -> i32 {
    println!("{}", value);
    value
}

하지만 다른 함수에 매개 변수로 함수를 전달하지 않습니다.

fn fun_test(value: i32, (some_function_prototype)) -> i32 {
    println!("{}", value);
    value
}

답변:


123

물론 넌 할 수있어:

fn fun_test(value: i32, f: &dyn Fn(i32) -> i32) -> i32 {
    println!("{}", f(value));
    value
}

fn times2(value: i32) -> i32 {
    2 * value
}

fn main() {
    fun_test(5, &times2);
}

이것은 Rust이기 때문에 클로저 의 소유권과 수명 을 고려해야합니다 .

TL; DR; 기본적으로 3 가지 유형의 클로저 (호출 가능한 객체)가 있습니다.

  1. Fn: 캡처 한 개체를 수정할 수 없습니다.
  2. FnMut: 캡처 한 객체를 수정할 수 있습니다.
  3. FnOnce: 가장 제한적입니다. 호출 될 때 자체 및 캡처를 소비하므로 한 번만 호출 할 수 있습니다.

클로저는 Fn, FnMut 및 FnOnce를 언제 구현합니까?를 참조하십시오 . 상세 사항은

클로저와 같은 간단한 함수 포인터를 사용하는 경우 캡처 세트가 비어 있고 Fn맛 이 있습니다.

더 멋진 일을하고 싶다면 람다 함수를 사용해야합니다.

Rust에는 C 에서처럼 작동하는 함수에 대한 적절한 포인터가 있습니다. 그 유형은 예를 들어 fn(i32) -> i32. Fn(i32) -> i32, FnMut(i32) -> i32그리고 FnOnce(i32) -> i32실제로 특징이다. 함수에 대한 포인터는 항상이 세 가지를 모두 구현하지만, Rust에는 함수에 대한 포인터 (캡처 세트가 비어 있는지에 따라)로 변환되거나 변환되지 않을 수도있는 클로저도 있지만 이러한 특성 중 일부를 구현합니다.

예를 들어 위의 예를 확장 할 수 있습니다.

fn fun_test_impl(value: i32, f: impl Fn(i32) -> i32) -> i32 {
    println!("{}", f(value));
    value
}
fn fun_test_dyn(value: i32, f: &dyn Fn(i32) -> i32) -> i32 {
    println!("{}", f(value));
    value
}
fn fun_test_ptr(value: i32, f: fn(i32) -> i32) -> i32 {
    println!("{}", f(value));
    value
}

fn times2(value: i32) -> i32 {
    2 * value
}

fn main() {
    let y = 2;
    //static dispatch
    fun_test_impl(5, times2);
    fun_test_impl(5, |x| 2*x);
    fun_test_impl(5, |x| y*x);
    //dynamic dispatch
    fun_test_dyn(5, &times2);
    fun_test_dyn(5, &|x| 2*x);
    fun_test_dyn(5, &|x| y*x);
    //C-like pointer to function
    fun_test_ptr(5, times2);
    fun_test_ptr(5, |x| 2*x); //ok: empty capture set
    fun_test_ptr(5, |x| y*x); //error: expected fn pointer, found closure
}

1
두 작품을 <F : Fn ...> 사용 여부 (.., f : & Fn ...) 사용에 차이가 있습니다. 알아야 할 세부 사항이 있습니까?
— Angel Angel

@AngelAngel : 글쎄요, Fn*특성 이니 , 보통 <T: Trait>대 (t: &T)적용됩니다. 비 제네릭 솔루션의 주요 제한 사항은 참조와 함께 사용해야한다는 것입니다. 따라서 FnOnce복사본으로 전달되어야 하는을 원하면 일반 스타일을 사용해야합니다.
— rodrigo 2011

5
대신 특성의 제네릭을 사용하는 것이 더 관용적이다 참고 (즉, 객체 <F: Fn..>대신에 (f: &Fn...)그리고이 이유입니다 -. 특성 객체는 동적 파견을 요구하면서 제네릭, 정적 디스패치가 발생합니다.
— 블라디미르 Matveev

3
흥미롭게도 인터페이스 (호출자) 관점에서 보면 FnOnce실제로 가장 일반적인 특성입니다. 캡처 된 상태의 읽기, 수정 또는 소유권에 관계없이 모든 클로저를 허용합니다. FnMut더 제한적이며 캡처 된 객체의 소유권을 갖는 클로저를 허용하지 않습니다 (그러나 여전히 상태 수정을 허용합니다). Fn캡처 된 상태를 수정하는 클로저를 허용하지 않기 때문에 가장 제한적입니다. 따라서 호출자 &Fn에게 가장 큰 제한을 두는 동시에 funTest호출자 f내부에서 호출 할 수있는 방법에 대한 제한을 최소화 해야합니다.
— user4815162342

30

Fn, FnMut및 FnOnce다른 대답 설명이다 폐쇄 유형. 범위에서 닫히는 함수 유형입니다.

클로저를 전달하는 것 외에도 Rust는 다음과 같은 간단한 (비 클로저) 함수 전달도 지원합니다.

fn times2(value: i32) -> i32 {
    2 * value
}

fn fun_test(value: i32, f: fn(i32) -> i32) -> i32 {
    println!("{}", f (value));
    value
}

fn main() {
    fun_test (2, times2);
}

fn(i32) -> i32여기에 함수 포인터 유형이 있습니다.

함수 유형으로 작업하는 것보다 완전한 클로저가 필요하지 않은 경우 이러한 클로저 수명 nicities를 처리 할 필요가 없기 때문에 종종 더 간단합니다.


이것은 구조체의 메서드와 함께 작동합니까?
— Ivan Temchenko

@IvanTemchenko 어쩌면? 다음은 플레이 할 수있는 몇 가지 코드입니다. play.rust-lang.org/…
— ArtemGr

그것은 정확히 내가 의미하는 것이 아닙니다 =) 자신의 상태를 캡처하는 dyn 클로저를 반환하는 해결 방법을 찾았으므로 인스턴스 참조를 전달할 필요가 없습니다 ...
— 이반 Temchenko에게
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.