Currently cx.use_resource() does a mutable borrow on the World, which makes it impossible to access two resources in the same presenter:
fn example(cx: Cx) -> impl View {
let res1 = cx.use_resource::<Res1>();
let res2 = cx.use_resource::<Res2>(); // ERROR
}
The reason this doesn't compile is that both calls to use_resource do a mutable borrow on cx, and return a value whose lifetime is tied to that borrow, which leads to a borrowing conflict. There are actually two reasons why use_resource needs mutable access.
The first reason is that use_resource internally calls self.vc.add_tracked_resource::<T>(), which inserts an entry into the TrackedResources ECS component. In short, it needs mutable access to the world.
The second reason is that use_resource and use_resource_mut call world.get_resource() and world.get_resource_mut(). The first only needs an immutable borrow, but the second requires a mutable one.
Now, from a user perspective, there's no reason why these calls should require a mutable borrow. Like reference counts, the tracking context is, and should be, invisible to the user; the fact that there's mutation going on under the hood should not impact the API.
Unfortunately, adding interior mutability (such as RefCell or Mutex) won't work here. These work fine for updating the tracking context, since the mutation doesn't require any long-lived borrows. But they won't help for returning the resource reference, because when you pull a reference out of something in a RefCell or Mutex, the lifetime is only as long as the lock() or borrow(). This means you can't return a long-lived object.
The right answer here would be to use WorldCell, which allows access to multiple resources. Unfortunately, WorldCell only handles resources (and events) and not entities/components. If it did, then we could modify Cx to simply hold on to a WorldCell instead of holding on to a World reference. However, because WorldCell doesn't currently support entities, we still need a World in order to mutate the tracking context, and we can't hold a mutable World and a WorldCell at the same time.
Apparently there's an open RFC to add entity support to WorldCell which would solve this problem.
Currently
cx.use_resource()
does a mutable borrow on the World, which makes it impossible to access two resources in the same presenter:The reason this doesn't compile is that both calls to
use_resource
do a mutable borrow oncx
, and return a value whose lifetime is tied to that borrow, which leads to a borrowing conflict. There are actually two reasons whyuse_resource
needs mutable access.The first reason is that
use_resource
internally callsself.vc.add_tracked_resource::<T>()
, which inserts an entry into theTrackedResources
ECS component. In short, it needs mutable access to the world.The second reason is that
use_resource
anduse_resource_mut
callworld.get_resource()
andworld.get_resource_mut()
. The first only needs an immutable borrow, but the second requires a mutable one.Now, from a user perspective, there's no reason why these calls should require a mutable borrow. Like reference counts, the tracking context is, and should be, invisible to the user; the fact that there's mutation going on under the hood should not impact the API.
Unfortunately, adding interior mutability (such as
RefCell
orMutex
) won't work here. These work fine for updating the tracking context, since the mutation doesn't require any long-lived borrows. But they won't help for returning the resource reference, because when you pull a reference out of something in aRefCell
orMutex
, the lifetime is only as long as thelock()
orborrow()
. This means you can't return a long-lived object.The right answer here would be to use
WorldCell
, which allows access to multiple resources. Unfortunately,WorldCell
only handles resources (and events) and not entities/components. If it did, then we could modifyCx
to simply hold on to aWorldCell
instead of holding on to aWorld
reference. However, becauseWorldCell
doesn't currently support entities, we still need aWorld
in order to mutate the tracking context, and we can't hold a mutableWorld
and aWorldCell
at the same time.Apparently there's an open RFC to add entity support to
WorldCell
which would solve this problem.