A function that consume itself and a Guard that contains a mutable reference to itself

The idea is that I have a Wrapper<T> where T : CleanUp, CleanUp being a trait that takes a &'wrapper mut Wrapper<T>.

Collector<'a, T>(&'wrapper mut Wrapper<T>) is meant to outlive its corresponding Wrapper<T>) such that it is responsible to call self.0.data.clean_up() (clean_up(...) being the method of CleanUp).

The intent is for Collector<'a, T> to do the clean up of the data instead of having wrapper to do that, as the clean up can only be done after a certain condition is met (think of database commit etc.).

Below is the Playground link that does not compile because once a mutable reference of a type is out, another method cannot consume that type together with the 'guard'.

1 Like

This is a contradiction in itself. A reference can't outlive its referent – that's the definition of a dangling reference.

1 Like

Good reply but that does not mean you should outright rule out that there might be a use case that might or need to utilize a similar pattern albeit in a different and as of now, unknown mechanism, which is the basis for asking this question on this forum.

The pattern is something along the line of delegating the cleaning up of an inner value of a wrapper to another struct (something like a guard) who holds a mutable reference to the wrapper and hence would be able to calls the clean up method of the inner value of the wrapper when its own drop is executed.

The wrapper should also have a method that consumes itself, together with the guard, and returns a new wrapper.

Basically, think of it as having a key to a treasure box. When creating the treasure box, the key is returned with the box. The key can unlock the box, momentarily, and returns a new box with a new key upon the end of the invariant scope (e.g. 'momentarily'). The last time AFTER you had unlocked the box, the key and the box are dropped simultaneously, but the box does not clean up inner value but the key does. Think of it as, if you lose the key, you might have the box (Rust guarantees that the box exists somewhere) but you have completely lost access to the content within the box, i.e. the clean up is as though it has been executed.

Maybe @CAD97 sir has a good idea on how I can go about doing this.

I think you have a XY problem here. You have a problem and you think your code snippet should solve it, but the code doesn't work and so you're focused on making it work rather than solving your problem. This might work sometimes, but if your code has a fundamental issue, like requiring a reference to outlive its borrow, then it will be very hard or even impossible to fix.

Mandatory clarification: "mutable" references are actually exclusive references. If the other struct holds an exclusive reference to the wrapper then nothing will be able to access that wrapper as long as that other struct exists, which however feels like what you want to do. Generally the solution is to just not try to access the wrapper directly, instead use the other struct to access the wrapper for as long as it exists.

This is very abstract and not that precise.

  • What happens if you try to open a box with a key that's not its own?
  • What happens if you "lose" the box but still have the key?
  • "Dropping simultaneously" is not a thing, one has to be dropped before the other.
5 Likes

I was able to tweak your playground into something that compiles by using Boxes instead of references:

To be honest, I suspect that this won't be suitable for your real use case for some reason or another, as I don't really understand what you're trying to do. Maybe, though, in describing why this isn't acceptable you'll be able to help the rest of us understand your requirements.

Looks like an attempt to find tracing GC which Rust developers have hidden somewhere.

Common mistake of newbies and that's a clarion call of trouble:

What does it even mean Rust guarantees that the box exists somewhere in the absence of GC? Rust doesn't play these games, either object have owner (or owners if Arc/Rc is employed) or it doesn't exist.

What does it even mean if you lose the key? Either you own the key or not. If you own it you couldn't lose it, it would be destroyed if you do, if you don't then someone else owns it.

Not even std::mem::forget changes that story: it just gives an ownership to the ultimate (and somewhat reckless) owner which keeps it alive till your program would exist (and thus cleanup would never be done).

Rust doesn't have GC, no matter how much you dig you wouldn't find it and if you have legitimate need to use GC (so far I have only find one use-case which legitimately demands it's use: implementation of language runtime for language with GC) then you would need to pick one of GC crates that exist out there, not try to force Rust compiler conjure one for you.

It just wouldn't work: the hardest thing of all is to find a black cat in a dark room, especially if there is no cat and the description, so far, looks precisely like such attempt.

Here's a potential restatement of what I think your needs are:

  • Wrapper provides mutable access to data.
  • Collector's drop handler calls data.cleanup; this is a mutating operation. This operation may be canceled.
  • Collector may outlive Wrapper, or vice-versa.

Since Wrapper and Collector both need access to data, data must outlive both. This is shared ownership. The easiest way to get this in Rust is to use Rc or Arc.

Since both Wrapper and Collector need mutable access to data, there needs to be some form of interior mutability since ownership is shared.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.