Mark LeMoine
Mark LeMoine

Reputation: 4618

Function pointers in Rust using constrained generics

I'm trying to create a struct that looks like this:

struct MediaLibrary<B>
where
    B: Ord,
{
    root_dir: PathBuf,
    item_meta_fn: String,
    self_meta_fn: String,
    media_item_filter: fn(&Path) -> bool,
    media_item_sort_key: fn(&Path) -> B,
}

The last two fields are meant to be used as a predicate to test if a given path is a valid media file and to sort a vector of paths (using sort_by_key), respectively.

However, as it is right now, the design is inflexible: both functions are fixed to accept only Path. I'd like to be able to use P: AsRef<Path> as the stdlib uses for many of its file and path methods, but I'm not sure how to add this.

A number of the methods I've impl'd for MediaLibrary are already using P: AsRef<Path> for their arguments, so my gut feeling tells me that there would be conflicts.

Upvotes: 2

Views: 3363

Answers (1)

Shepmaster
Shepmaster

Reputation: 430673

To my knowledge, you cannot have a function pointer with a generic type, I don't even think such a construct is accepted by the Rust parser.

Furthermore, you cannot simply switch to extra type parameters on the struct as they would be unused by the struct itself:

struct MediaLibrary<F, P1, K, P2, B>
where
    F: Fn(P1) -> bool,
    P1: AsRef<Path>,
    K: Fn(P2) -> B,
    P2: AsRef<Path>,
    B: Ord,
{
    root_dir: PathBuf,
    item_meta_fn: String,
    self_meta_fn: String,
    media_item_filter: F,
    media_item_sort_key: K,
}
error[E0392]: parameter `P1` is never used
 --> src/main.rs:3:24
  |
3 | struct MediaLibrary<F, P1, K, P2, B>
  |                        ^^ unused type parameter
  |
  = help: consider removing `P1` or using a marker such as `std::marker::PhantomData`

Instead, you can choose to apply the constraints only on the functions where they are used:

struct MediaLibrary<F> {
    media_item_filter: F,
}

impl<F> MediaLibrary<F> {
    fn do_filter<P>(&self)
    where
        F: Fn(P) -> bool,
        P: AsRef<Path>,
    {}
}

As the message states, you could also use PhantomData.

Upvotes: 4

Related Questions