Active Record pattern in Rust

I have an idea to make a library in Active Record design (for external REST service):

let user = client.user("id");
user.description = "new description";
user.save();

So, an object should have an original response and updated data.
I thought that it can be implemented using Deref with DerefMut but I have a lot of problems with constructions like this:

    struct UserWrapper {
        origin_dto: RefCell<UserDto>,
        inner_dto: RefCell<UserDto>,
    }

    impl UserWrapper {
        pub fn new(origin_dto: UserDto) -> UserWrapper {
            let inner_dto = origin_dto.clone();
            let inner_dto = RefCell::new(inner_dto);
            // let inner_dto = Rc::new(RefCell::new(inner_dto));
            // let origin_dto = Rc::new(RefCell::new(origin_dto));
            let origin_dto = RefCell::new(origin_dto);
            UserWrapper { origin_dto, inner_dto }
        }
    }

    impl DerefMut for UserWrapper {
        fn deref_mut(&mut self) -> &mut Self::Target {
            // &mut self.inner_dto
            // self.inner_dto.clone().get_mut();
            // self.inner_dto.borrow_mut().
            // let dto = self.inner_dto.clone().into_inner();
            // &mut UserDto::default()
            doesn't work :(
        }
    }

    impl Deref for UserWrapper {
        type Target = UserDto;

        #[inline]
        fn deref(&self) -> &Self::Target {
            let x = self.inner_dto.clone().borrow();
            // &self.origin_dto
            x.deref()
        }
    }

How it should be implemented without code generation?

The RefCell makes things difficult, because it won’t let you have an unwrapped reference very easily. If you don’t need the RefCell for something else, you could do something like this (untested):

struct Record<DTO> {
    origin: Rc<DTO>,
    inner: Rc<DTO>
}

impl<DTO> Record<DTO> {
    pub fn new(origin: DTO): Self {
        let origin = Rc::new(origin);
        Record { inner: Rc::clone(origin), origin }
    }
}

impl<DTO> Deref for Record<DTO> {
    type Target=DTO;
    fn deref(&self)->&DTO {
        &*self.inner
    }
}

impl<DTO> DerefMut for Record<DTO>
where DTO: Clone {
    fn deref(&mut self)->&mut DTO {
        Rc::make_mut(&mut self.inner)
    }
}

If your records are small, however, you might get better performance by proactively cloning them in new, which avoids the heap allocation of the Rc: (also untested)


struct Record<DTO> {
    origin: DTO,
    inner: DTO
}

impl<DTO: Clone> Record<DTO> {
    pub fn new(origin: DTO): Self {
        Record { inner: origin.clone(), origin }
    }
}

impl<DTO> Deref for Record<DTO> {
    type Target=DTO;
    fn deref(&self)->&DTO {
        &self.inner
    }
}

impl<DTO> DerefMut for Record<DTO> {
    fn deref(&mut self)->&mut DTO {
        &mut self.inner
    }
}

For the reasons @2e71828 pointed out, the active record pattern is not well-suited for Rust APIs. In general, the Active Record pattern is hard to implement correctly and completely; except for the most trivial cases, there are just so many details that are practically impossible to get right.

For instance, if you want to ensure that all instances of a type referring to the same entity get updated, you'll need to implement some sort of notification or publisher-subscriber system – but what do you do if some other clients also have similar data? You might send a request to them, but your Internet connection might fail in the middle of the request and you might not even know whether they got the update.

There's actually a mathematically-proved counterpart to these sorts of problems, called the CAP theorem, which basically states that you can't have your lunch and eat it, too.

For these reasons, I usually suggest not trying to implement Active Record. If you need to modify stuff, send modification requests, and pull back the updated, fresh data only at the point of consumption, where you immediately need it.

1 Like