可以将带引用的函数作为将提供拥有值的闭包参数传 递吗?

问题描述 投票:1回答:2

我试图简化我的闭包,但是当参数由闭包所有但内部函数调用只需要一个引用时,我在将闭包转换为对相关函数的引用时遇到了问题。

#![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可以在这种场景中使用,但它不是我正在努力的真实场景。

rust closures
2个回答
1
投票

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

有一次性的解决方法,如rodrigo's answer所示,但一般的解决方案是简单地自己参考,就像你做的那样:

something_taking_a_closure(|owned_value| some_function_or_method(&owned_value))

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


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

.for_each(|consumed_struct| MyStruct::my_method(&consumed_struct))

1
投票

我认为还没有特质for_each_refIterator。但你可以很容易地写自己的(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));调用包装函数。

© www.soinside.com 2019 - 2024. All rights reserved.