是否可以将带有引用的函数作为提供自有值的闭包参数传​​递?

时间:2019-04-12 13:31:49

标签: rust closures

我正在尝试简化闭包,但是当参数由闭包拥有但内部函数调用仅需要引用时,将我的闭包转换为对关联函数的引用时遇到问题。

#![deny(clippy::pedantic)]

fn main() {
    let borrowed_structs = vec![BorrowedStruct, BorrowedStruct];

    //Selected into_iter specifically to reproduce the minimal scenario that closure gets value instead of reference
    borrowed_structs
        .into_iter()
        .for_each(|consumed_struct: BorrowedStruct| MyStruct::my_method(&consumed_struct));
    // I want to write it with static method reference like following line:
    // for_each(MyStruct::my_method);
}

struct MyStruct;
struct BorrowedStruct;

impl MyStruct {
    fn my_method(prm: &BorrowedStruct) {
        prm.say_hello();
    }
}

impl BorrowedStruct {
    fn say_hello(&self) {
        println!("hello");
    }
}

Playground

是否可以简化此代码:

into_iter().for_each(|consumed_struct: BorrowedStruct| MyStruct::my_method(&consumed_struct));

到以下内容:

into_iter().for_each(MyStruct::my_method)

请注意,这里的into_iter仅适用于我拥有闭包中的值的情况。我知道iter可以在这种情况下使用,但这不是我正在研究的实际情况。

2 个答案:

答案 0 :(得分:1)

我认为特征for_each_ref中还没有Iterator。但是您可以很容易地编写自己的(playground)

trait MyIterator {
    fn for_each_ref<F>(self, mut f: F)
    where
        Self: Iterator + Sized,
        F: FnMut(&Self::Item),
    {
        self.for_each(|x| f(&x));
    }
}
impl<I: Iterator> MyIterator for I {}
borrowed_structs
    .into_iter()
    .for_each_ref(MyStruct::my_method);

另一种选择,如果您能够更改my_method函数的原型,则可以使其通过值或借用引用来接受值:

impl MyStruct {
    fn my_method(prm: impl Borrow<BorrowedStruct>) {
        let prm = prm.borrow();
        prm.say_hello();
    }
}

然后您使用.for_each(MyStruct::my_method)的原始代码就可以了。

第三个选择是使用通用包装函数(playground)

fn bind_by_ref<T>(mut f: impl FnMut(&T)) -> impl FnMut(T) {
    move |x| f(&x)
}

然后使用.for_each(bind_by_ref(MyStruct::my_method));调用包装的函数。

答案 1 :(得分:1)

您的一般问题的答案是。在将函数作为闭包参数传​​递时,类型必须完全匹配。

有一种一次性的解决方法,如rodrigo's answer所示,但是一般的解决方案是像执行操作一样,自己简单地获取引用:

java.lang.IllegalArgumentException: Parameter specified as non-null is null: method kotlin.jvm.internal.Intrinsics.checkParameterIsNotNull, parameter exception
at com.myproject.custom.assertion.TraderViewAssertion$Companion$hasCorrectBackgroundColorAtPosition$1.check(TraderViewAssertion.kt)
at androidx.test.espresso.ViewInteraction$SingleExecutionViewAssertion.check(ViewInteraction.java:419)
at androidx.test.espresso.ViewInteraction$2.call(ViewInteraction.java:282)
at androidx.test.espresso.ViewInteraction$2.call(ViewInteraction.java:268)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5417)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)

实际上,advocated for this case about two years ago是人体工程学改造的一部分,但似乎没有其他人对此感兴趣。


在特定于 的情况下,您可以从闭包参数中删除该类型以使其更加简洁:

something_taking_a_closure(|owned_value| some_function_or_method(&owned_value))