问题
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.
回答1:
Is it possible
Sure it is. You have to fix the multiple cascading errors first:
- Invalid syntax:
Vec<_>::removeisn't valid. - Incompatible argument types:
Vec::removemodifies aVec, so you have to pass in aVecsomehow. - Mutability:
Vec::removemodifies aVec, so you have to declare that the function is allowed to do so. Vec::removereturns 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.
回答2:
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.
来源:https://stackoverflow.com/questions/37391695/is-it-possible-to-pass-an-object-method-as-argument-to-a-function-and-bind-it-to