0
votes

I'm starting with Rust and trying to implement a struct like the following, where I have a vector of predicate functions:

pub struct Client<P>
where
    P: Fn(&Deal) -> bool,
{
    clientid: String,
    api_url: String,
    deal_filters: Vec<P>,
}

I managed to cobble this together based on various things, and I was previously able to implement a new function for these structs when I just hard-code this attribute to an empty vector, but it gets weird when I try construct one of these with real functions:

impl<P> Client<P>
where
    P: Fn(&Deal) -> bool,
{
    pub fn new(stings: settings::Settings, filters: Option<Vec<P>>) -> Self {
        let deal_filters = match filters {
            Some(fltrs) => fltrs,
            None => vec![deal_predicates::check_revenue],
        };
        Self {
            api_url: stings.url,
            clientid: stings.clientid,
            deal_filters
        }
    }

Indeed, when I try to pass these "default" filters in, I get the following compiler error:

`match` arms have incompatible types
expected type `Vec<for<'r> fn(&'r Deal) -> _>`
 found struct `Vec<for<'r> fn(&'r Deal) -> _ {check_revenue}>`rustcE0308

In addition, I'm not sure how to declare the type for an instance. I'm unclear on the type I am declaring this Client<P> to be.

The following is based on trying to follow guidance from the compiler, but it's telling me that the size can't be known at compile time:

let client: clients::Client<dyn Fn(&Deal) -> bool>
    = clients::Client::new(cfg, None);

14 |         = client: clients::Client::new(cfg, None);
   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time

My goal was to provide a set of predicate functions that can be used and offer different predicates for different contexts. I believe I could potentially use Box to solve the "size can't be known at compile time" problem?

Of course, there are certainly other ways I can solve this problem. In addition, a lot of my thinking about this is based on Haskell, so I probably have the wrong expectations or assumptions here, but I would appreciate suggestions as to a better way to achieve what I want.


Edit: Fixed Version Below

Thanks to some help below, I wrapped up my functions in Box and then my new problem was the error expected fn pointer, found reference. I also simplified it to remove the generic trait declaration.

Once I googled and resolved the expected fn pointer, found reference error, my code compiled fine. This solution is unfamiliar to me, so I am unsure if it's idiomatic, but it seems to work.

Struct:

pub struct Client {
    clientid: String,
    api_url: String,
    deal_filters: Vec<fn(&Deal) -> bool>,
}

impl new:

impl Client {
    pub fn new(
        stings: settings::Settings,
        filters: Option<Vec<fn(&Deal) -> bool>>,
    ) -> Self {
        let deal_filters = match filters {
            Some(fltr) => fltr,
            None => vec![
                deal_predicates::check_revenue as fn(&Deal) -> bool,
            ],
        };
        Self {
            api_url: stings.url,
            clientid: stings.clientid,
            deal_filters,
        }
    }
}

I also got it working with Boxing the dyn Fn(&Deal) -> bool types.

1

1 Answers

1
votes
Client<P>
  where
    P: Fn(&Deal) -> bool,

What this says is that a given Client struct has one type that is callable. But different lambdas are different types.

So yes, what you really want is a Vec<Box<dyn Fn(&Deal) -> bool>>.