How to define a pointer to a trait function in Rust

Viewed 250

I apologize if my question has already been answered or is somewhere in the documentation, but I haven't been able to find the answer. Suppose I have the following code:

trait Saluter {
    fn hello(&self);
    fn bye(&self);
}

struct A {}
struct B {}

impl Saluter for A {
    fn hello(&self) {
        println!("Hello A!");
    }

    fn bye(&self) {
        println!("Bye A!");
    }
}

impl Saluter for B {
    fn hello(&self) {
        println!("Hello B!");
    }

    fn bye(&self) {
        println!("Bye B!");
    }
}

pub struct Foo {
    saluter: Box<dyn Saluter>,
    salute: fn (&dyn Saluter),
}

fn main() {
    let x = Foo {saluter: Box::new(A{}), salute: Saluter::hello};
    let y = Foo {saluter: Box::new(B{}), salute: Saluter::bye};
    (x.salute)(x.saluter.borrow()); //Should print "Hello A!"
    (y.salute)(y.saluter.borrow()); //Should print "Hello B!"
}

Basically I'm trying to independently manipulate the caller and the method being called. However I get the following compilation error:

let x = Foo {saluter: Box::new(A{}), salute: Saluter::hello};
   |                                                  ^^^^^^^^^^^^^^ one type is more general than the other
   |
   = note: expected fn pointer `for<'r> fn(&'r (dyn Saluter + 'r))`
              found fn pointer `for<'r> fn(&'r dyn Saluter)`

The same error applies for y obviously. I have only recently started learning Rust so I am not at all fluent with Rust lifetimes parameters but it seems to me that it's complaining about that, though I couldn't find what (dyn Salute + 'r) would mean exactly.

Is there a way to achieve what I want or is this simply not possible in Rust? In C++ I could do it with pointers to Base class easily but I am struggling to find how to achieve this in Rust.

Thanks for the help!

2 Answers

You use a generic type bound by Saluter in your Foo struct:

pub struct Foo<T> where T: Saluter {
    saluter: Box<T>,
    salute: fn (&T),
}

Playground

I was able to get your code to compile by just constraining the function pointer type:

pub struct Foo {
    saluter: Box<dyn Saluter>,
    salute: fn (&(dyn Saluter + 'static)),
}

What this additional + 'static constraint means is that the salute function pointer stored in Foo can only be used with types implementing Saluter which do not have limited lifetimes — loosely speaking, "do not contain non-static references". That is, if you had

struct B<'x> { ... }
impl<'x> Saluter for B<'x> {}

then the function wouldn't be usable. However, this is an uncommon case.

Unfortunately, I'm not sure why the function pointer Saluter::hello can't be the fully general type which was implied by your original code,

    salute: for<'a> fn (&'a (dyn Saluter + 'a)),

so I can't give a complete explanation of it.

Related