[SOLVED] Function parameter lifetime, where for syntax?, Fn trait confusion


#1

Goal: Understand the following block of code.

fn find_entities<F>(entities: &Vec<Entity>, f: F) -> Vec<Entity>                                                       
where for<'r> F: (Fn(&'r &Entity) -> bool)                                                                             
{                                                                                                                      
    entities.iter()                                                                                                    
            .filter(f)                                                                                                 
            .map(|x| (*x).clone())                                           
            .collect::<Vec<_>>()                                                                                       
} 

It began as this:

fn find_entities(entities: &Vec<Entity>) -> Vec<Entity>                                                                                                           
{                                                                                                                      
    entities.iter()                                                                                                    
            .filter(|e| e.kind == EntityType::Foo)                                                                                                 
            .map(|x| *x)                                           
            .collect::<Vec<_>>()                                                                                       
}

Then I tried to add a function parameter.

fn find_entities<F>(entities: &Vec<Entity>, f: F) -> Vec<Entity> 
where F: Fn(&Entity) -> bool                                                                                                        
{                                                                                                                      
    entities.iter()                                                                                                    
            .filter(f)                                                                                                 
            .map(|x| *x)                                           
            .collect::<Vec<_>>()                                                                                       
}

However the compiler complained. (Please note: I’m writing all this up after the fact and salvaging the right compiler errors was difficult. They may not be exact so consider things more of a guide than exactly what I was seeing.)

error[E0277]: the trait bound `for<'r> F: std::ops::FnMut<(&'r &Entity,)>` is not satisfied
  --> src/main.rs:24:14
   |
24 |             .filter(f)
   |              ^^^^^^ the trait `for<'r> std::ops::FnMut<(&'r &Entity,)>` is not implemented for `F`
   |
   = help: consider adding a `where for<'r> F: std::ops::FnMut<(&'r &Entity,)>` bound

After struggling a bit to make sense of this error message a google search turned up the following on StackOverflow: Stack Overflow post

Though the compiler suggest the following, I was not able to make it work.

where for<'r> F: std::ops::Fn<(&'r &Entity,)>

It took several tries to arrive at the solution.

where for<'r> F: (Fn(&'r &Entity) -> bool)

After even more googling to figure out what all this means I stumbled upon Rust nomicon section on trait bounds, which is the first time I’ve even seen the where for syntax used outside of the compiler.

So clearly I’ve stumbled onto something above my current pay grade. What I think is happening is that because my parameter f is being passed to filter, which operates on an iterator lifetimes are involved. So the trait bound needs to specify a lifetime.

At this point I’m wondering if I’ve done something really obtuse or backward to require so much. I assume if I simply use iterator in place and just pass in the closure then things would be significantly more simple.

Any clarity or information on this at all would be appreciated. It is not an easy thing to google for and find more information.


#2

Okay so the key was learning Higher Rank Trait Bounds, or HRTBs from the Rustonomicon.

A google for that led to: https://stackoverflow.com/questions/35592750/how-does-for-syntax-differ-from-a-regular-lifetime-bound

Which is a perfect explanation, for almost my exact usage.


#3

Thank you for adding to the things googlers in the future can fined to get helpful links!


#4

Note that this is (apparently) one of those places where making a new closure can simplify things:

fn find_entities<F>(entities: &Vec<Entity>, f: F) -> Vec<Entity>                                                       
where F: Fn(&Entity) -> bool
{                                                                                                                      
    entities.iter()                                                                                                    
            .filter(|x| f(x))
            .cloned()
            .collect()
}

https://play.rust-lang.org/?gist=342b0a19f43b9cba2c5818fcf4691395&version=stable