Build error: "cannot move out of borrowed content"


#1

Could somebody help me to fix this build error? I tried to use .clone() but unfortunately this Option type doesn’t support this clone method.

error[E0507]: cannot move out of borrowed content
  --> src/main.rs:43:3
   |
43 |         *self.sync_error.read().unwrap()
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot move out of borrowed content

The code is put in Rust Play Ground

And also paste here:

use std::sync::RwLock;

#[derive(Debug)]
pub enum Error1 {
	InvalidTotalKernelSum,
	Other(String)
}

#[derive(Debug)]
pub enum Error2 {
	NotFoundErr(String),
	SerErr(String),
}


#[derive(Debug)]
pub enum Error {
	Core(Error1),
	Store(Error2),
}

#[derive(Debug)]
pub struct SyncState {
	sync_error: RwLock<Option<Error>>,
}

impl SyncState {
	pub fn new() -> SyncState {
		SyncState {
			sync_error: RwLock::new(None),
		}
    }
    
    /// Communicate sync error
	pub fn set_sync_error(&self, error: Error){
		let mut sync_error = self.sync_error.write().unwrap();
		*sync_error = Some(error);
	}
	
	/// Get sync error
	pub fn sync_error(&self) -> Option<Error> {
		*self.sync_error.read().unwrap()
	}

}

fn main() {
    let state = SyncState::new();
    println!("state={:?}", state);
}

#2

Change this to self.sync_error.read().unwrap(). The method read on RWLock takes &self, no need to dereference here.


#3

Thanks! but not work, if change like that, error will be:

expected enum `std::option::Option`, found struct `std::sync::RwLockReadGuard`

#4

Could somebody help me on the fix? :bouquet::bouquet::bouquet:


#5

Sure, your function wants to return an Option<Error>, but read on a RWLock returns a LockResult<RwLockReadGuard<Error>>, which you then unwrap into a RwLockReadGuard<Error>, which is a different thing. You’ll need turn it into an Option<Error>.

I think I see where this is going. You were using * to get out the error and wanted to return that. It won’t work, since your errors aren’t Copy, and your sync_error takes a reference to self. You might want to try returning an &Option<Error>


#6

Did you see the link of the Rust Playground? you can try it in playground. still not working.


#7

Yes I saw the link, but I can’t take the time right now to fully fix it.


#8

You can’t leak a value protected by a lock into a plain reference (the point of the lock would be defeated if that were allowed). You need to return either the RwLockReadGuard<Error> or change your API to receive a closure from the caller to which you pass a reference of the Error.

If you want to hide the fact there’s a RwLockReadGuard specifically, you can leverage impl Trait:

/// Get sync error
	pub fn sync_error<'a>(&'a self) -> impl std::ops::Deref<Target=Option<Error>> + 'a {
		self.sync_error.read().unwrap()
	}

#9

Thanks @vitalyd, that works :+1: :clap::clap::clap:


#10

@vitalyd but in the caller side, calling *sync_state.sync_error() will cause dead-lock issue?


#11

You can cause a deadlock if you hold the read lock and then try to acquire a write lock; some combination of this may panic instead. But this is just how RwLock is implemented, it’s not specific to sync_error().

In fact, you probably don’t want to hide the fact you’re holding a read lock behind an impl trait type - I thought you had some local operations you wanted to encapsulate, but if you’re holding locks across more code, you’ll want to be pretty explicit about it.