Run Code after DerefMut to enforce correct state

Original Problem

I was recently having the following problem: I have a simulation tool which has different Storage solutions to save results on the disk (eg. json, xml, etc.). This number might significantly increase in the future. To specify, how to store results, I wanted to be able to store with different solutions simultaneously. Furthermore, to read out the results, order is important as this defines the priority.

The code which I produced initially looked like this:

enum StorageOption {
    XML,
    JSON,
}

fn store_results(storage_options: Vec<StorageOption>) {...}

However, I noticed that this does not accurately represent what I want to do. I could in principle do this:

let storage_options = vec![StorageOption::XML, StorageOption::XML];
store_results(&storage_options);

which is not the point, since I only want to specify the StorageOption::XML once.
This meant, I was in search of a datastructure which has unique elements but preserves order of elements.

Current Solution

My simplest solution was to define a UniqueVec<T> struct which checks when pushing new values.

struct UniqueVec<T>(Vec<T>);

impl<T> UniqueVec<T> {
    fn push(&mut self, element: T) -> Option<T>
    where
        T: PartialEq,
    {
        if self.0.contains(&element) {
            Some(element)
        } else {
            self.0.push(element);
            None
        }
    }
}

But to also exploit all the functionality of the Vec<T> class of the standard library, I also implemented Deref.

impl<T> core::ops::Deref for UniqueVec<T> {
    type Target = Vec<T>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

Can I use DerefMut?

This was the point when I thought, I could also reimplement my push(&mut self) method by implementing the DerefMut trait and always enforcing that only unique elements are inside the inner Vec<T>. However, since fn deref_mut(&mut self) -> &mut Self::Target returns a mutable reference, there is no option to execute code afterwards. Is there any way that I can achieve to have the full functionality of the Vec<T> type but with explicit checking of uniquenes of elements?

Thanks in advance for your ideas.

You likely want an indexmap::IndexSet then.


The alternative would be defining a guard type (kinda like MutexGuard you know how that works), which implements DerefMut to Vec<T> and restores the "unique" property in its Drop implementation. Something like this Rust Playground

Could something like this work ?

pub struct Foo<'a, T> {
  inner: &'a mut Vec<T>
}

impl <'a, T> Drop for Foo<'a, T> {
  ... code you want to run ...
}

impl Deref / DerefMut for Foo<'a, T> { ... }

It is similar to your idea, except instead of wraping the Vec, we wrap a &mut Vec, and when the wrapper drops, it runs the code you want to run.

I was not aware of this datatype. This looks very interesting to me. However, elements are required to implement Hash (which is not a problem when you simply have enum Variants, but could be a problem for other use-cases). I will definitely look into that! Thanks

Yes that's one way to achieve it. However, I would prefer a more simple Syntax. Obtaining a Mutex and releasing it is very cumbersome if you simply want to have a Vector.

But you don't simply want a vector though! You want a vector that contains different items, which is different.

Assuming you want to expose the full Vec interface by implementing Deref/DerefMut (because otherwise you can just re-implement all the methods you need), what necessarily has to happen is:

  • the users deref your container to a Vec (can't happen implicitly because you'll also want to do fixup work afterwards, so it needs a method call);
  • then the user does what it wants with the Vec;
  • finally you fixup the Vec according to your type's invariants.

Compare it to a Mutex workflow:

  • the users locks the Mutex (can't happen implicitly for obvious reasons, but also because it'll need to do work afterwards, so it needs a method call);
  • then the user does what it wants with the Mutex's protected value;
  • finally the MutexGuard unlocks the Mutex.

I don't see how the interface of your vector could be much different than that a Mutex given they have pretty much the same workflow.

1 Like

Yes this is exactly what needs to happen. I also tried the suggestions made by @zeroexcuses but was not able to come up with a good implementation.

For now my problem is small enough that I will stick to reimplementing some of the methods which I desire.

This is what I had in mind:

pub struct Foo<'a, T> {
    inner: &'a mut Vec<T>,
}

impl<'a, T> Drop for Foo<'a, T> {
    fn drop(&mut self) {
        // mut code to run
    }
}

impl<'a, T> Deref for Foo<'a, T> {
    type Target = Vec<T>;

    fn deref(&self) -> &Self::Target {
        &self.inner
    }
}

impl<'a, T> DerefMut for Foo<'a, T> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.inner
    }
}

I am curious to learn where this fails.

This code compiles but how do we construct the type? We want Foo to actually own the Vec<T> in order to fully encapsulate it.

Right now we can only do

let mut new_vec = vec![1_usize];
let foo = Foo { inner: &mut new_vec };

We could accept that the type Foo does not own its values. But I do not like this idea. Passing the value between functions is a lot more complicated if we do so.

Furthermore, the Drop trait only runs once the whole value is dropped. It can not be implemented on References directly (without using a Mutex-like type as @SkiFire13 suggested).

Have a look at this code sample, which uses your suggestions. The println!("Cleanup"); statement should run everytime a new push(..) method is called but it only runs once the value drops at the end of the main function.

All in all, I liked your suggestions but I do not think it is possible to solve this problem with these methods if we want to have ownership of the struct.

What I had in mind was this:

pub struct Bar {
... other fields ...
  inner: Vec<T>
}

impl Bar {
  fn get_mut_vec(&mut self) -> Foo<'a, T> {
    Foo { .. }
  }
}

The situation here is that when the Foo is dropped, we run this cleanup / stabilization method you want to run.

I misunderstood your problem. If you want to run some function after every .push(..) call -- I do not see a way to guarantee that (besides manually implementing push which calls vec.push then calls your code). But to say "pull in all of Vec::funcs , but only modify Vec::push .. I don't know how to express that"

Yes this works but is extremely similar to the approach described by @SkiFire13 . There is a 2nd type involved just as with Mutex. So in the end I cannot use (eg.) unique_vec.push(1) but need to call unique_vec.get_mut_vec().push(1). A working solution but I hoped to have an API which is somewhat that of a regular Vec<T>.

I believe this is done one level up, so the comparison should be

Blah.get_mut_vec().push(1)

vs

Blah.inner.push(1)

Yes and no. If I implement the push method myself as I have done so far, my code above works.