Getting an owned transaction back out of an Rc<RefCell<...>>

I'm writing database code that creates a transaction, puts it inside an Rc<RefCell<...>>, and passes that to functions that create data. Then I want to commit the transaction which requires me to have an owned transaction, but every way I try to get that owned transaction, I get the error "error[E0507]: cannot move out of an Rc". I've included the code below. The code is using the sqlx crate, version 0.6.3 (I haven't moved to a later version so I don't introduce a new set of complexities until the current set are resolved).

         let tx: Transaction<Postgres> = match self.rt.block_on(self.pool.begin()) {                
             Err(e) => return Err(anyhow!(e.to_string())),        
             Ok(t) => t,                                                                              
         };
         let transaction = Rc::new(RefCell::new(tx));
         if !self.model_tables_exist(Some(transaction.clone()))? {
             self.create_tables(Some(transaction.clone()))?;
             self.create_base_data(Some(transaction.clone()))?;
         }
         self.do_database_upgrades_if_needed(Some(transaction.clone()))?;
         self.create_and_check_expected_data(Some(transaction.clone()))?;

         if let Err(e) = self.rt.block_on((*trans).into_inner().commit()) {
             return Err(anyhow!(e.to_string()));
         }
         Ok(())

...and that code gets the error:

error[E0507]: cannot move out of an `Rc`
   --> src/model/postgres/postgresql_database.rs:656:42
    |
656 |         if let Err(e) = self.rt.block_on((*trans).into_inner().commit()) {
    |                                          ^^^^^^^^ ------------ value moved due to this method call
    |                                          |
    |                                          move occurs because value has type `std::cell::RefCell<sqlx::Transaction<'_, sqlx::Pos
    |
note: `std::cell::RefCell::<T>::into_inner` takes ownership of the receiver `self`, which moves value
   --> /usr/obj/ports/rust-1.72.1/rustc-1.72.1-src/library/core/src/cell.rs:805:29

Before I tried using Rc<RefCell<Transaction>> I passed around a &Option<&mut Transaction> , but this led to other problems -- complications with more required explicit lifetime annotations, and inability to sufficiently/accurately dereference it when I needed a mutable transaction to pass to sqlx calls (a long story). So changing to Rc<...> solved some problems but this error message also arose, and I'm trying to figure out how to do it better and make the compiler happy. I've read in the stdlib docs and tried a variety of other syntax but without success so far.

Suggestions/questions welcome. Thanks much!

Rc shares the content, so you can’t destroy its contents, because some other instance may want to use it.

But you can replace an Option with None, so wrap the transaction in an Option and use take() to remove it.

Alternatively, there’s Rc::into_inner (a function, not a method) that can give you back its contents if it’s the last and only Rc that has it.

2 Likes

Thank you very much. That seems to work (I have to fix other compile errors and see if the tests run). My main mistake seems to have been to miss the fact that, as you point out, Rc::into_inner is an associated function, and when I called .into_inner as a method, it was really calling it on the RefCell, which explains the error message I was getting. Thanks again.

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.