Passing a context to a function inside an object that needs the parent to do something

I'm facing an issue with the design of a piece of code.

Basically I would like to have a function on an object that takes something that looks like a context so that the function can then call functions of other objects inside the context.
I believe this could be a poor design and of course it's not working.
Here's an example:

What would it be the idiomatic way to achieve something like that?

Fundamentally you cannot have mutable references that overlap unless one is a borrow of the other. Mutable references must be unique — this is one of the fundamental rules of Rust. Consider:

pub fn process(&mut self, ctx: &mut Context) {
    let ss = &mut;
    for s in ss {

Here &mut self is also accessible through ctx. What if process changed the contents of the systems variable? That for loop would not be happy.

You need to structure your code such that you never have overlapping mutable references. The process method only needs access to components, so don't pass it a type with access to systems too.

You are right and I definitely understand your point, but in the real world scenario this method

impl System {
    pub fn process(&mut self, ctx: &mut Context) {
        println!("{}", ctx.scene.get_component(0));

will also need to access a lot of other variables that are in Context, not just the components.
That's why I find it hard to restructure this code in a way that it doesn't bring around stuff that it doesn't need.

The idea here is that in the final design I want to have a loop and inside that loop the Scene calls process on all its systems. But the systems are going to be different structs that implement a trait. So each different system will need to access components and other variables that live inside the Context. That way I can kind of "distribute" this as a library and let the user write his own systems that can do whatever they need with the Context.
Does it make any sense? :slight_smile:

Perhaps you should be creating contexts that are specialized for each subset of fields you want, e.g.

struct Context<'a> {
    components: &'a mut Vec<Component>,
    // or &'a mut [Component] if you do not need to modify the length

This would allow your Scene to create the context:

pub fn process(&mut self) {
    let ss = &mut;
    let ctx = Context { components: &mut self.components };
    for s in ss {

This way process is guaranteed not to touch the systems vector.

Alternatively you can use std::mem::take, which replaces the vector in scene with an empty vector. Then it's not dangerous for process to modify the systems vector, because it's not the vector you're currently iterating through. This still doesn't allow creating the context outside of process as that would still be overlapping mutable references.

Your description makes me think of the Reader pattern. In Haskell :: e -> a where e is environment/context. Each process would implement a function that accepts the ctx, take what it needs to generate the return value of the process.

Right now I have no idea how that plays in Rust. What I do know is that we only need to read the ctx.

I assume based on how you are describing the object with a processes, that by generating anything new using the context that the object has to be mutable (a global env of sorts)...

Yes, what you say does make sense and it would work quite easily in Rust too if it were a read-only variable. Actually I'm not mutating ctx itself but rather something contained within the properties of its properties.

In the end I just split up Context in way that it doesn't contain the Scene object. That way I can do something like scene.process(ctx) with no troubles as scene isn't part of ctx itself. The function inside Scene is then passing self together with ctx to the underlying objects and it works as expected because I have both Scene and everything else contained in Context available in the final function.

Does that not mean you are in fact mutating ctx? (e.g., we cannot have multiple read-only ctx references).

It sounds like what you ended up doing is redesign ctx such that it only needs to be read-only. You did this by removing the scene property from ctx? If so, now you have something like ctx -> processed scene (a Reader) where the body of the function constructs not yet processes scene, the subject of the Reader when called with ctx.

It's more like ctx is still mutable as I need to mutate stuff in there.
Scene, on the other hand, doesn't need to be mutable.
Taking Scene out of ctx actually let me avoid a call like this:

ctx.scene.process(&mut ctx)

that becomes:

scene.process(&mut ctx)

And then inside Scene's process I can just do:, ctx)

Which, in turn, calls out to my user-defined functions that can mutate ctx however they need.

So, ctx is your state that the user-defined functions can mutate.

Context -> Context -> Context etc..

where each arrow represents a user-defined function mutating the memory where Context is stored (aka state-machine that uses an endomap, a function that changes internal state whilst remaining type Context).

Yes that describes my use-case pretty well :slight_smile:

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.