【问题标题】:Can a function that takes a reference be passed as a closure argument that will provide owned values?一个接受引用的函数是否可以作为提供自有值的闭包参数传​​递?
【发布时间】:2019-04-12 13:31:48
【问题描述】:

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

#![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 可以在这种情况下使用,但这不是我正在处理的真实情况。

【问题讨论】:

  • 如果您能够更改 my_method 签名,则可以将其写为 fn my_method(prm: impl Borrow<BorrowedStruct>) 并接受按值和按引用。
  • 注意:静态方法在 Rust 中通常称为associated functions
  • @rodrigo,您能否分享一下&BorrowedStructimpl Borrow<BorrowedStruct> 之间的差异的参考
  • 当然! official documentation 既完整又令人困惑(至少对我而言)。 TL;DR;问题是Borrow<T> 既适用于T,也适用于&T,甚至适用于Cow<'a,T>
  • 此外,它是一个关联函数这一事实与这里无关——同样的问题也适用于常规函数。

标签: rust closures


【解决方案1】:

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

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

something_taking_a_closure(|owned_value| some_function_or_method(&owned_value))

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


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

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

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 不错,但实际上我宁愿在闭包中明确调用它,.for_each(|x|MyStruct::my_method(&amp;x));
    • @ÖmerErden:当然,我也更喜欢你的选择。但是 OP 明确询问如何简化此代码。您可以争辩说我的回答并没有简化代码,但它实际上复杂化...
    • @rodrigo,我个人更喜欢按照 Ömer Erden 的建议保留代码。恕我直言,为引用编写这种特定的函数对我来说是一个小样板
    猜你喜欢
    • 2021-02-12
    • 2020-03-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-08
    • 1970-01-01
    相关资源
    最近更新 更多