Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to pass an object method as argument to a function and bind it to the object?

Tags:

rust

Is it possible to make a bind to object method? For example, I have a vector and a lot of functions which do something if some item exists in the vector. I would implement it as follows:

fn perform_if_exists(item: u8, vector: &Vec<u8>, func: fn(usize)) {
    let idx = vector.iter().position(|i| *i == item );
    match idx {
        Some(i) => func(i), 
        None    => {},
    }
}

fn main() {
    let v: Vec<u8> = vec![1, 2, 3];
    perform_if_exists(1, &v, Vec<_>::remove);
}

but it gives a lot of errors. I think they are reasonable but it's because I don't understand how to put vector's method as argument to a function.

like image 697
Victor Polevoy Avatar asked May 23 '16 12:05

Victor Polevoy


People also ask

Can you pass an object into a function?

Just as passing objects to functions as arguments is an efficient way of moving information to where it's needed, so is using objects as return values. Functions can either manipulate objects that you pass to them and then return them, or they can return brand-new objects that they create in the function body.

How do you bind an object in JavaScript?

JavaScript Function bind() With the bind() method, an object can borrow a method from another object. The example below creates 2 objects (person and member).

How do you pass an argument to an object?

To pass an object as an argument we write the object name as the argument while calling the function the same way we do it for other variables. Syntax: function_name(object_name); Example: In this Example there is a class which has an integer variable 'a' and a function 'add' which takes an object as argument.

What does bind () method of?

The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.


2 Answers

Is it possible

Sure it is. You have to fix the multiple cascading errors first:

  1. Invalid syntax: Vec<_>::remove isn't valid.
  2. Incompatible argument types: Vec::remove modifies a Vec, so you have to pass in a Vec somehow.
  3. Mutability: Vec::remove modifies a Vec, so you have to declare that the function is allowed to do so.
  4. Vec::remove returns the removed value, so you have to allow the function to return a value, even if it's thrown away.
fn perform_if_exists<F, R>(item: u8, vector: &mut Vec<u8>, func: F)
where
    F: Fn(&mut Vec<u8>, usize) -> R,
{
    let idx = vector.iter().position(|i| *i == item);
    if let Some(i) = idx {
        func(vector, i);
    }
}

fn main() {
    let mut v = vec![1, 2, 3];
    perform_if_exists(1, &mut v, Vec::remove);
    println!("{:?}", v);
}

I switched to a generic as that's generally how you will accept closures. A function pointer is fine but more restrictive.

like image 54
Shepmaster Avatar answered Oct 23 '22 21:10

Shepmaster


A method in Rust is nothing more than a function, which also takes a first self parameter. The method Vec::remove takes two arguments: &mut self and index: usize. The self parameter is always of type Self, which is Vec<u8> in this case. The complete type of Vec::<u8>::remove is: fn(&mut Vec<u8>, usize) -> u8 (yes it also returns the removed element).

After changing the type in your code (+ a few minor mistakes), it works:

//                                      vvv-- has to be mutable
fn perform_if_exists(item: u8, vector: &mut Vec<u8>, func: fn(&mut Vec<u8>, usize) -> u8) {
    let idx = vector.iter().position(|i| *i == item );
    match idx {
        Some(i) => { 
            func(vector, i);
        }, 
        None    => {},
    }
}

fn main() {
    let mut v: Vec<u8> = vec![1, 2, 3];
    perform_if_exists(1, &mut v, Vec::remove);
}

But fn(...) -> ... types are raw pointer types and just work for ordinary functions. Often you also want to enable the user to pass anything that is "callable", like closures. There are traits exactly for that purpose: Fn(...) -> ....

Let me propose another solution:

fn perform_if_exists<T, F, R>(item: T, vector: &mut Vec<T>, func: F) -> Option<R>
    where F: FnOnce(&mut Vec<T>, usize) -> R,
          T: PartialEq
{
    let idx = vector.iter().position(|i| *i == item );
    idx.map(|i| func(vector, i))
}

This solution is far more generic as it allows arbitrary item types, arbitrary "callable" types and returns the value that is returned by the given function. Note that the main function didn't change; the solution is more generic, but all old uses still work.

like image 4
Lukas Kalbertodt Avatar answered Oct 23 '22 19:10

Lukas Kalbertodt