I'm struggling with a decorator pattern where I want to count the number of invocations of a method without making intrusive modification to the method implementation.
Here is the smallest example I can think of. It doesn't compile and the compiler error shows where I'm stuck -- Not being able to specify a Fn
-bound generic argument.
mod lib {
// Library code, which I have 0 control over.
pub trait Thing {
fn do_something(&self);
}
pub trait Factory {
type Th: Thing;
fn get_thing(&self) -> Self::Th;
}
pub fn run<F: Factory>(f: &F) {
let thing = f.get_thing();
thing.do_something();
}
}
mod _impl {
// My implementation code,
// which I prefer not to change just to add instrumentation.
use super::lib::{Thing, Factory};
pub struct ThingImpl;
impl Thing for ThingImpl {
fn do_something(&self) {
println!("ThingImpl::do_something");
}
}
pub struct FactoryImpl;
impl Factory for FactoryImpl {
type Th = ThingImpl;
fn get_thing(&self) -> Self::Th {
ThingImpl {}
}
}
}
mod instrument {
// Instrumentation decorator,
// which can instrument my implementations
// via its public API.
use std::cell::RefCell;
use super::lib::{Thing, Factory};
pub struct ThingWithDecorator<Th, Deco> {
inner: Th,
decorator: Deco,
}
impl<Th: Thing, D: Fn()> Thing for ThingWithDecorator<Th, D> {
fn do_something(&self) {
(self.decorator)();
self.inner.do_something()
}
}
pub struct FactoryWithCounter<Inner> {
inner: Inner,
count: RefCell<usize>,
}
impl<Fty> FactoryWithCounter<Fty> {
pub fn count(&self) -> usize {
*self.count.borrow()
}
fn inc_counter(&self) {
let mut count = self.count.borrow_mut();
*count += 1;
}
}
impl<F: Factory> Factory for FactoryWithCounter<F> {
type Th = ThingWithDecorator<F::Th, _>;
fn get_thing(&self) -> Self::Th {
ThingWithDecorator {
inner: self.inner.get_thing(),
decorator: || {
self.inc_counter()
},
}
}
}
pub trait FactoryExt : Factory {
fn with_counter(self) -> FactoryWithCounter<Self> where Self: Sized {
FactoryWithCounter {
inner: self,
count: Default::default(),
}
}
}
impl<F: Factory> FactoryExt for F {}
}
fn main() {
use instrument::FactoryExt;
let factory = _impl::FactoryImpl {};
let factory = factory.with_counter();
lib::run(&factory);
println!("do_something called {} times", factory.count());
}
Errors:
Compiling playground v0.0.1 (/playground)
error[E0121]: the placeholder `_` is not allowed within types on item signatures for associated types
--> src/main.rs:81:45
|
81 | type Th = ThingWithDecorator<F::Th, _>;
| ^ not allowed in type signatures
For more information about this error, try `rustc --explain E0121`.
error: could not compile `playground` (bin "playground") due to 1 previous error