How can I pass generic function as parameter?

I want to test a function that can accept a AsRef parameter, which can be call with &str or String, but I don't know the correct way to write it.

fn test<T, F>(fun: F) where T: AsRef<str>, F: Fn(T) -> bool {
    assert_eq!(fun("abc"), true);
    assert_eq!(fun("abc".to_string()), true);
}

fn is_ascii<T: AsRef<str>>(s: T) -> bool {
    s.as_ref().is_ascii()
}

fn not_empty<T: AsRef<str>>(s: T) -> bool {
    !s.as_ref().is_empty()
}

fn main() {
    test(is_ascii);
    test(not_empty);
}

What you wrote currently says that it accepts a closure which takes some specific type T, for which you know that T implements AsRef<str>. However what you want is that it should accept any type T that implements AsRef<str>. These are not the same things.

Unfortunately this kind of for-all bound is only possible with lifetimes, not types.

Currently the only way to get something like generic closures / function types is to create your own Fn-trait alternatives and implement those manually.

trait FnAsRefStrToBool {
    fn apply<T: AsRef<str>>(&self, s: T) -> bool;
}

fn test<F>(fun: F)
where
    F: FnAsRefStrToBool,
{
    assert_eq!(fun.apply("abc"), true);
    assert_eq!(fun.apply("abc".to_string()), true);
}

fn is_ascii<T: AsRef<str>>(s: T) -> bool {
    s.as_ref().is_ascii()
}

fn not_empty<T: AsRef<str>>(s: T) -> bool {
    !s.as_ref().is_empty()
}

struct IsAscii;
impl FnAsRefStrToBool for IsAscii {
    fn apply<T: AsRef<str>>(&self, s: T) -> bool {
        is_ascii(s)
    }
}
struct NotEmpty;
impl FnAsRefStrToBool for NotEmpty {
    fn apply<T: AsRef<str>>(&self, s: T) -> bool {
        not_empty(s)
    }
}

fn main() {
    test(IsAscii);
    test(NotEmpty);
}
2 Likes

Thank you for your help.

It seems that a postponed RFC will make this possible.