Akiner Alkan
Akiner Alkan

Reputation: 6918

Can a function that takes a reference be passed as a closure argument that will provide owned values?

I am trying to simplify my closures, but I had a problem converting my closure to a reference to an associated function when the parameter is owned by the closure but the inner function call only expects a reference.

#![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

Is it possible to simplify this code:

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

To the following:

into_iter().for_each(MyStruct::my_method)

Note that into_iter here is only to reproduce to scenario that I own the value in my closure. I know that iter can be used in such scenario but it is not the real scenario that I am working on.

Upvotes: 1

Views: 251

Answers (2)

Shepmaster
Shepmaster

Reputation: 432089

The answer to your general question is no. Types must match exactly when passing a function as a closure argument.

There are one-off workarounds, as shown in rodrigo's answer, but the general solution is to simply take the reference yourself, as you've done:

something_taking_a_closure(|owned_value| some_function_or_method(&owned_value))

I actually advocated for this case about two years ago as part of ergonomics revamp, but no one else seemed interested.


In your specific case, you can remove the type from the closure argument to make it more succinct:

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

Upvotes: 1

rodrigo
rodrigo

Reputation: 98516

I don't think there is a for_each_ref in trait Iterator yet. But you can write your own quite easily (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);

Another option, if you are able to change the prototype of the my_method function you can make it accept the value either by value or by reference with borrow:

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

And then your original code with .for_each(MyStruct::my_method) just works.

A third option is to use a generic wrapper function (playground):

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

And then call the wrapped function with .for_each(bind_by_ref(MyStruct::my_method));.

Upvotes: 1

Related Questions