Explicitly specify FnMut trait bound argument to member variable

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?

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.

1 Like

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.

1 Like

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.

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
    |

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?

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),
//
}
}

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.

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.

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)).