Explicitly specify FnMut trait bound argument to member variable


#1
struct A;

#[derive(Debug,Clone)]
pub struct FilteredPages<'a, F> where F: for<'q> FnMut(&'q V) -> bool  {
    buckets: VecDeque<A>,
    my_filter: &'a F,
}

impl<'a, F> FilteredPages<'a, F>
where
    F: for<'q> Fn(&'q V) -> bool,
{
    pub fn new(my_filter: &'a F) -> Self {
        Self {
            buckets: VecDeque::<A>::new(),
            my_filter: my_filter,,
        }
    }
}

pub struct Book<'a, F> where F: for<'q> FnMut(&'q A) -> bool {
    aaaa: FilteredPages<'a,  F>,
}


impl<'a,F> Book<'a,F> where F: for<'q> FnMut(&'q A) -> bool {
    pub fn new() -> Book<'a> {
        Self {
            aaaa: FilteredPages::new(&|_x : &A| {
                true
            }),
        }
    }

I actually would like to get rid of the generic F within Book such that it stays internal, but how do I explicitly specify the type of F for aaaa?


#2

In this particular example, you can define Book as:

struct Book {
   aaaa: FilteredPages<fn(&A) -> bool>
}

impl Book {
   fn new() -> Self {
      fn filter(_: &A) -> bool { true }
      Self { aaaa: FilteredPages::new(filter) }
   }
}

But the way your code is setup right now, it may get unwieldy if you want to do this type of thing without using function pointers.


#3

What about using a boxed function instead of generics, then adding a type alias to make writing signatures a little easier. So something along the lines of this:

type Filter = Box<FnMut(&A) -> bool + 'static>;

struct Book {
   aaaa: FilteredPages<Filter>
}

(I’m on my phone, so that may have a couple typos)

If it were me I’d probably want to add a lifetime to the Filter instead of 'static so you can use a closure that references outer variables, but that’s personal choice.


#4

The problem is that Box<FnMut> is not an FnMut, and so the generic type bound in FilteredPages will not allow this. This is what I was alluding to when I said things will get unwieldy.

If there’s no (performance) need for FilteredPages to take generic closures, then just dropping the generic type there and stuffing the closure in a Box would work. If there’s a need to work with boxed and unboxed filters, then I think a custom trait could be defined and implemented for F: FnMut(...) -> bool and for some wrapper struct that holds a boxed closure; then FilteredPages can be bound to that trait.


#5

The Box-ing part is pretty annoying. It just raises more questions.

error[E0277]: the trait bound `for<'r> std::ops::FnMut(&'r A) -> std::option::Option<usize>: std::marker::Sized` is not satisfied
   --> src/book.rs:130:47
    |
130 |         self.                             (Box::new(filter))
    |                                               ^^^^^^^^ `for<'r> std::ops::FnMut(&'r A) -> std::option::Option<usize>` does not have a constant size known at compile-time
    |
    = help: the trait `std::marker::Sized` is not implemented for `for<'r> std::ops::FnMut(&'r A) -> std::option::Option<usize>`
    = note: required by `<std::boxed::Box<T>>::new`

since the FnMut is not implementing sized…

and ontop of that:

error[E0277]: the trait bound `for<'r> std::ops::FnMut(&'r A) -> bool + 'static: std::marker::Sync` is not satisfied
   --> src/main.rs:114:32
    |
114 |                         scoped.execute(move || {
    |                                ^^^^^^^ `for<'r> std::ops::FnMut(&'r A) -> bool + 'static` cannot be shared between threads safely
    |

#6

What is filter there? Please show more code when asking for help.

As for the Sync issue, assuming you really do want to share a reference across threads, you need to specify an additional bound on the FnMut: FnMut(&A) -> bool + Sync + ‘static

Taking a step back, what are you trying to achieve exactly? Do you need FilteredPages to be generic in some contexts or no?


#7

I was confused, assuming the type Filter = Fn<...> specified a type and not a trait bound.

Essentially what I want to do is the following

type RetainClosure = ... + Send + Sync;

struct Sub {
      core : Vec<u8>,
      retain_closure: RetainClosure,
};

impl Sub {
fn push_data(&mut self, data, Vec<u8>) {
      // push data
      self.core.retain(self.retain_closure);
}
}

pub struct Wrapper {
 subs : HashMap<String,Sub>,
retain_closure: RetainClosure,
}

impl Wrapper {
fn new() -> Self {
    Self { subs : HashMap::new(), retain_closure: RetainClosure }
}

fn more(&mut self, key : String) {
     let _ =self.subs.entry(key).or_insert(Sub::new(self.retain_closure));
}

fn push_data(key : String, data: Vec<u8>) {
//  match  self.subs.entry(key);
//      Entry::Occupied(x) -> x.exnted(data),
//
}
}

#8

type Filter = Fn<...> creates an alias for the Fn<...> portion, but it does not define a new type. It’s essentially syntactic shorthand to refer to a longer type definition.

I don’t think what you sketched above will work if you want to use this closure in Vec::retain calls. The reason is what I mentioned upthread: Box<FnMut(...) -> ..> is not itself an FnMut(...) -> ....

The most ergonomic and straightforward approach is to make Sub generic on the closure type, but I guess you want to avoid that? The other alternative will be to have Sub hold references to a closure, but now you’re going to get lifetime proliferation.


#9

At this point honestly I don’t care anyore, how I make it wok - I have been down the lifetime rabbit borrow a couple of times,.
Convertin Sub into Sub<F> where F: RetainClosure would be ok, as long as wrapper is not going to have to do the same.


#10

Well, actually, if this all boils down just to calling retain then you can just store the Box<FnMut> and then call retain(|x| (self.retain_closure)(x)).