Traits and Traits
In Rust, you can use trait to define an interface consisting of:
- related types
- related constants
- related functions.
and you can use the following features:
- as compilation time limits for general parameters
- as types, behind links or pointers.
However ... only some traits can be used directly as types. Those traits that have the designation Object Safe.
It is now considered unsuccessful that there is one trait keyword to define both fully functional and object-safe attributes.
Interlude: How does dispatch at runtime work?
When using a feature as type: &Trait , Box<Trait> , Rc<Trait> , ... at run time, a thick pointer is used consisting of:
- data pointer
- virtual pointer.
A method call is sent through a virtual pointer to a virtual table.
For such characteristics as:
trait A { fn one(&self) -> usize; fn two(&self, other: usize) -> usize; }
for type X , the virtual table will look like (<X as A>::one, <X as A>::two) .
Thus, scheduling of runtime is performed:
- Choosing the right table element
- calling it a pointer and data arguments.
This means that <X as A>::two looks like this:
fn x_as_a_two(this: *const (), other: usize) -> usize { let x = unsafe { this as *const X as &X }; x.two(other) }
Why can't I use any attribute as a type? Which object is safe?
This is a technical limitation.
There are many features features that cannot be implemented for runtime mailings:
- related types
- related constants
- related common functions
- related functions with
Self in signature. - ... maybe others ....
There are two ways to signal this problem:
- early: refuse to use
trait as a type if it has any of the above functions, - late: refuse to use any of the above
trait types as a type.
For now, Rust prefers to signal the problem earlier: traits that do not use any of the above functions are Object Safe calls and can be used as types.
Non-object safety features cannot be used as types, and an error is triggered immediately.
Now what?
In your case, just switch from compile-time polymorphism to run-time polymorphism for the method:
pub trait Messenger : Sync + Send { fn send_embed(&self, u64, &str, f: &FnOnce(String) -> String) -> Option<u64>; }
There are a few wrinkles: FnOnce requires moving from f , and it is borrowed here, so instead you need to use FnMut or Fn . FnMut is the following more general method, therefore:
pub trait Messenger : Sync + Send { fn send_embed(&self, u64, &str, f: &FnMut(String) -> String) -> Option<u64>; }
This makes the Messenger object of the Safe Object object and therefore allows you to use &Messenger , Box<Messenger> , ...