zman
zman

Reputation: 223

Rust calling trait alias for closure

I am using a trait to create an alias for a closure so that I do not have to repeat the declaration several times throughout the code, however I cannot work out how to cast the trait back to Fn so that I can call it in main, is this possible?

trait Closure: Send + Sync {}
impl <F: Send + Sync> Closure for F where F: Fn(&str) -> bool {}

pub struct Struct {
    pub closure: Box<dyn Closure>
}

impl Struct {
    pub fn new(closure: impl Closure + 'static) -> Self {
        Self{
            closure: Box::new(closure)
        }
    }
}

pub fn main() {
    let s = Struct::new(|f: &str| f.is_empty());

    let result: bool = (s.closure)("hello world"); //TODO
    println!("{}", result );
}

Upvotes: 4

Views: 231

Answers (2)

Kevin Reid
Kevin Reid

Reputation: 43753

As Ibraheem Ahmed's answer points out, Closure doesn't guarantee in general that any type implementing it is a function. But you can do that by adding Fn as a supertrait:

pub trait Closure: Send + Sync + Fn(&str) -> bool {}

With this one line changed, your code will compile and run.

The difference between this and the other option of adding an explicit call method to the trait is here, Closure can only be implemented for closures and function pointers — not for structs or enums — because in stable Rust (as of version 1.47.0) there is no way to write an impl Fn for MyType. But if you only intend to use this trait with actual closures like |f: &str| f.is_empty(), that's no problem.

Upvotes: 3

Ibraheem Ahmed
Ibraheem Ahmed

Reputation: 13518

You are trying to call a Box<dyn Closure> as a method. At that point, all the compiler knows about this type is that it implements Closure. It does not know if it is a function, a struct, or anything else, which is why your code will not compile.

You can get solve this by adding an associated method to the Closure trait. This is a very common way of modelling functions:

pub trait Closure: Send + Sync {
    fn call(&self, input: &str) -> bool;
}

You can implement it for the Fn type:

impl <F: Send + Sync> Closure for F where F: Fn(&str) -> bool {
    fn call(&self, input: &str) -> bool {
        self(input)
    }
}

And call the Closure trait object by simply invoking its call method:

let s = Struct::new(|f: &str| f.is_empty());
let result: bool = s.closure.call("hello world");

Upvotes: 3

Related Questions