Cannot call function with parameter with matching trait in select crate resulting in confusing errors

Hi, I have this struct with HTML "selectors" in it.

pub struct SelectAll {
    result: &'static dyn Predicate,
    title: &'static dyn Predicate,
    summary: &'static dyn Predicate,
    url: (&'static dyn Predicate, UrlLocation),
}

Those are predicates from the select HTML parsing crate. (select::predicate - Rust)
However, when I try to use it with the find function I get this weird error that I don't understand. (select::document::Document - Rust)

error[E0277]: expected a `std::ops::Fn<(&select::node::Node<'_>,)>` closure, found `dyn select::predicate::Predicate`
  --> src/rover/all/executor/method/select.rs:30:19
   |
30 |     for result in document.find(predicates.result) {
   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected an `Fn<(&select::node::Node<'_>,)>` closure, found `dyn select::predicate::Predicate`
   |
   = help: the trait `std::ops::Fn<(&select::node::Node<'_>,)>` is not implemented for `dyn select::predicate::Predicate`
   = note: required because of the requirements on the impl of `std::ops::FnOnce<(&select::node::Node<'_>,)>` for `&dyn select::predicate::Predicate`
   = note: required because of the requirements on the impl of `select::predicate::Predicate` for `&dyn select::predicate::Predicate`
   = note: required because of the requirements on the impl of `std::iter::Iterator` for `select::document::Find<'_, &dyn select::predicate::Predicate>`

Do you have any idea how to do this?

The issue is that even though dyn Predicate : Predicate (i.e., the unifying dyn Predicate type obtained from concrete implementors / types that implement Predicate by erasing their types and using dyn-amic dispatch in its stead, does implement the Predicate type itself), that crate does not provide the "intuitive" &dyn Predicate : Predicate that is required for you:

/// The `::select` crate is missing this:
impl<P : ?Sized> Predicate for &'_ P
where
    P : Predicate,
{ … }
  • With P = dyn Predicate, the above would grant you the required
    &'_ (dyn Predicate) : Predicate bound.

The crate, however, offers an impl that is just as useful, if not more useful:

impl<F> Predicate for F
where
    F : Fn(&Node) -> bool,
{ … }

This means that the moment you can forge a |node: &'_ Node| -> bool { … } closure, you can use that as a predicate.

Thus, the following ought to work:

Solution

- for result in document.find(predicates.result) {
+ for result in document.find(|n: &Node| predicates.result.matches(n)) {

If you need to write this too often, you can factor out the trick:

// #[derive(Derivative)] /* To make it `Copy` */
// #[derivative(Copy(bound=""), Clone(bound=""))]
struct ByRef<'lt, P : ?Sized + Predicate>(&'lt P);

impl<P : ?Sized + Predicate> Predicate
    for ByRef<'_, P>
{
    fn matches (self: &'_ Self, node: &'_ Node)
      -> bool
    {
        P::matches(&*self.0, node)
    }
}

so as to be able to write:

for result in document.find(ByRef(predicates.result)) {
  • (or you can even have the struct ByRef-wrap each field).

You can even make ByRef() become a method:

trait PredicateByRef : Predicate {
    fn by_ref (self: &'_ Self)
      -> ByRef<'_, Self>
    {
        ByRef(self)
    }
}

impl<P : ?Sized + Predicate> PredicateByRef for P {}

This way, you can write:

for result in document.find(predicates.result.by_ref()) {
4 Likes

Thanks a lot. Saved me!