runtime_injector
runtime_injector copied to clipboard
No examples for interior mutability except for the constant
Hey, I started playing around with this DI container but I'm unable to achieve interior mutability. Could you provide some examples how that could be achieved with both Svc
modes (Rc
/Arc
) and:
-
Cell
, -
RefCell
, -
Mutex
, -
RwLock
, - or any other wrapper of any kind?
I've only found the example for constant with the Mutex in one of the docstrings:
builder.provide(constant(Mutex::new(0i32)));
I would like to be able to provide particular component wrapped for example by Mutex
and then be able to retrieve this Mutex
from the container.
I've created small example with comments what I would like to be able to achieve:
use runtime_injector::*;
use std::cell::{Cell, RefCell};
use std::error::Error;
use std::sync::{Mutex, RwLock};
pub trait Foo: Service {
fn increase(&mut self);
}
#[derive(Default)]
struct FooImpl(u64);
impl Foo for FooImpl {
fn increase(&mut self) {
self.0 += 1;
}
}
pub trait Bar: Service {
fn increase(&mut self);
}
struct BarImpl {
foo: Svc<dyn Foo>,
}
impl BarImpl {
// Not possible to get Cell/RefCell/Mutex/RwLock
pub fn new(foo: Svc<dyn Foo>) -> Self {
Self { foo }
}
}
impl Bar for BarImpl {
fn increase(&mut self) {
self.foo.increase();
}
}
interface!(
dyn Foo = [FooImpl],
dyn Bar = [BarImpl]
);
fn main() -> Result<(), Box<dyn Error>> {
let mut builder = Injector::builder();
// No obvious way to allow interior mutability, like auto creation of Cell/RefCell/Mutex/RwLock
builder.provide(FooImpl::default.singleton().with_interface::<dyn Foo>());
builder.provide(BarImpl::new.singleton().with_interface::<dyn Bar>());
// It could be available for example in such way:
builder.provide(FooImpl::default.wrap_in_mutex().singleton().with_interface::<dyn Foo>());
// or:
builder.provide(FooImpl::default.wrap_using(|result| Mutex::new(result)).singleton().with_interface::<dyn Foo>());
let injector = builder.build();
// Not able to call these as they require &mut
injector.get::<Svc<dyn Foo>>()?.increase();
injector.get::<Svc<dyn Bar>>()?.increase();
// No Cell/RefCell/Mutex/RwLock availability
injector.get::<Cell<Svc<dyn Bar>>>()?.increase();
injector.get::<RefCell<Svc<dyn Bar>>>()?.increase();
injector.get::<Mutex<Svc<dyn Bar>>>()?.increase();
injector.get::<RwLock<Svc<dyn Bar>>>()?.increase();
Ok(())
}
Mutex<Svc<dyn Foo>>
(or other similar interior mutability types) is probably not what you want. In this case, you'd be guaranteeing mutually exclusive access to the shared pointer to the data, not to the data itself. This wouldn't give you access to call &mut self
methods in dyn Foo
. Instead, you probably want a Svc<Mutex<dyn Foo>>
to be returned by the injector (for example).
I think this should be doable with new providers like you mentioned. An .in_mutex
/.in_rc
/etc. suite of extensions might be nice to have. Personally I think implementations of the interface should have interior mutability instead (meaning Foo::increase
would instead take &self
and FooImpl
would hold an AtomicU64
, for example), but I'm sure there are use cases where having a mutex/refcell/etc provided for you would be nice.
On second thought, even just a .map
function for service factories could be useful and would cover even more situations:
builder.provide(FooImpl::default.map(Mutex::new).singleton());
I'm not sure how this could handle interfaces though.