Callbacks or alternatives

I'm working on a hobby project and wondering about the best way to handle callback methods for it or if there is a clean alternative to using callbacks.

Here's what I have at the moment: A struct implementing a virtual machine exposed via a library crate. It is independent of any front-end implementation details ( os/platform, cli vs gui vs web, etc ). The front-end is provided by an application using the library by implementing a trait. Running the machine will call back to the front-end to perform input/output, and diagnostics. It is extremely likely there will never be more than one implementation in a single application, so dynamic dispatch is not needed. Most of the VM methods are generic with respect to the trait and take a mutable reference to it as an argument.

This all works well, but I have a minor dislike of the degree to which the generic front-end parameter has become ubiquitous within the library. I reduced the noise a bit by using impl trait in argument position, which seems a decent use of the feature, but I was wondering if there might be a cleaner way. One thing I definitely don't want to do: Store callback closures in the VM itself.

That is fairly standard.

That is also the typical alternative to not storing the closure. On the other hand, the user doesn't have to see it in most cases, so it's not that bad.

Yeah, I hadn't found anything cleaner than the impl trait, and I admit it's only a minor annoyance. I was just hoping there might be a way to reduce the number of places I needed it.

I don't like storing closures in this case for a few reasons:
It complicates serializing/deserializing the VM, as the front-end closures should not be part of that.
It leads to ownership hassles in the front-end, e.g. needing RefCell to allow mutability of various parts of the front-end through multiple closures.