Cannot move out of borrowed content take 2

Hello,

I'm somewhat new to rust. Usually I'm able to fix errors myself. This time I'm lost.

Here's my code:

#![feature(nll)]

use std::collections::HashMap;
use std::thread;
use std::time::Duration;

fn main() {
   let mut clients: HashMap<String, &Clients> = HashMap::new();
   thread::spawn(move || loop {
   	thread::sleep(Duration::from_millis(1000));
   	//receives new events from event loop
   	//forward each event to specific module
   	module_executor(&mut clients, "12345".to_string());
   });
}

pub struct Data<'a, 'b, 'c: 'a>
{
   pub clients: &'a mut HashMap<String, &'c Clients>,
   pub session_id: &'b str,
}

pub struct SystemClient {
   pub session_id: String,
   pub ip: String,
}

impl SystemClient {
   pub fn stop(&mut self) {}
   pub fn start(&mut self) {}

   pub fn new(session_id: String, ip: String) -> Self {
   	SystemClient {
   		session_id,
   		ip,
   	}
   }
}

//I'm using #[cfg(feature = "system")]... to build specific modules.
pub struct Clients { 
   pub system: Option<SystemClient>,
       //pub module2: Option<Module2Client> //...
}

impl Clients {
   pub fn new() -> Self {
   	Clients {
   		system: None,
   	}
   }

   pub fn upgrade_system(&mut self) {
   	if self.system.is_none() {
   		let new_client = SystemClient::new("6789".to_string(), "".to_string());
   		self.system = Some(new_client);
   		self.system.unwrap().start();
   	}
   }

   pub fn downgrade_system(&mut self) {
   	if self.system.is_some() {
   		self.system.take().unwrap().stop();
   		self.system = None;
   	}
   }
}

fn module_executor<'a, 'b>(mut clients: &'b mut HashMap<String, &'a Clients>, session_id: String) {
   //define a handy data struct, makes function signatures easier to read. any function "f" accepts Data
   let call_data = Data { clients: &mut clients, session_id: &session_id };
   //choose between different functions and call them (in this example only f exists)
   f(call_data);
}

fn f(call_data: Data)
{
   let ip = 123;
   //get a client by session id
   let my_client = &call_data.clients.get_mut(call_data.session_id).unwrap();
   //change some value
   let system = &mut my_client.system.unwrap();
   system.ip = ip.to_string();
}
error[E0507]: cannot move out of borrowed content                        ] 0/1: testt                                                                                      
 --> src/main.rs:55:4
  |
55 |             self.system.unwrap().start();
  |             ^^^^^^^^^^^ cannot move out of borrowed content

error[E0507]: cannot move out of borrowed content
 --> src/main.rs:80:20
  |
80 |     let system = &mut my_client.system.unwrap();
  |                       ^^^^^^^^^^^^^^^^ cannot move out of borrowed content

error: aborting due to 2 previous errors

What can I do? Thanks!

Use self.system.as_mut().unwrap().start() etc.

1 Like

99% of the time this error means you have to use as_ref() or as_mut().

You're getting it because unwrap() on Option<Foo> wants to give you Foo, rather than &Foo. Option<Foo> can be converted to Option<&Foo> with as_ref(), and then it's fine to take &Foo out of it.

When Rust says "move" it means the value can exist only in one place, and you wanted it in a new place (e.g. moving from self.system to unwrap()'s result). Therefore the old location has to be destroyed, and everything that refers to the destroyed value must be destroyed too. When you call self.system.unwrap().start(), then system.unwrap() wants to destroy system (since the value is moved out of it), and self.system can't refer to a "hole" after unwrapping, so self would have to be destroyed too. But obviously, it can't.

On the other hand unwrapping Option<&Foo> is fine, because instead of moving the owned content out of it and destroying the Option, it can be copied instead. &Foo is easily copyable, because many references can exist at the same time.

2 Likes

A few other odds and ends ...

downgrade_system() can be written as follows:

pub fn downgrade_system(&mut self) {
     if let Some(mut s) = self.system.take() {
         s.stop();
     }
 }

Option::take() sets the option to None, so there's no need to do that explicitly. The above also avoids a needless unwrap() (you generally want to try and avoid them).

Because f() tries to mutate the Client, the clients map will need to hold mutable references to Client. You also don't need as many lifetime parameters as you have. Something like the following ought to do:

pub struct Data<'a, 'b: 'a> {
    pub clients: &'a mut HashMap<String, &'b mut Clients>,
    pub session_id: &'a str,
}

You can elide the lifetime parameters in module_executor. In addition, clients parameter doesn't need to have a mut modifier. So the fn can look like:

fn module_executor(
    clients: &mut HashMap<String, &mut Clients>,
    session_id: String,
) {
    let call_data = Data {
        // No need to `&mut clients` again - `clients` is already a mutable reference.
        // Compiler will automatically insert a reborrow, which is
        // as-if you wrote `&mut *clients`
        clients, 
        session_id: &session_id,
    };
    f(call_data);
}
2 Likes

Thank you all! Your replies got me to compile the program without errors.

Thanks again :smiley: , here is the updated code. Now (of course) I get a new error "borrowed value does not live long enough". I am not completely sure I understand why it happens. And how to fix it.

//#![feature(nll)]

use std::collections::HashMap;
use std::thread;
use std::time::Duration;

fn main() {
	let mut clients: HashMap<String, &mut Clients> = HashMap::new();

	thread::spawn(move || loop {
		let session_id = "12345".to_string();

		thread::sleep(Duration::from_millis(1000));
		//receives new events from event loop
		//forward each event to specific module
		module_executor(&mut clients, session_id);
	});
}

pub struct Data<'a, 'b: 'a> {
	pub clients: &'a mut HashMap<String, &'b mut Clients>,
	pub session_id: &'a str,
}

pub struct SystemClient {
	pub session_id: String,
	pub ip: String,
}

impl SystemClient {
	pub fn stop(&mut self) {}
	pub fn start(&mut self) {}

	pub fn new(session_id: String, ip: String) -> Self {
		SystemClient {
			session_id,
			ip,
		}
	}
}

pub struct Clients {
	pub system: Option<SystemClient>,
}

impl Clients {
	pub fn new() -> Self {
		Clients {
			system: None,
		}
	}

	pub fn upgrade_system(&mut self, new_client: SystemClient) {
		if self.system.is_none() {
			self.system = Some(new_client);
			self.system.as_mut().unwrap().start();
		}
	}

	pub fn downgrade_system(&mut self) {
		if let Some(mut s) = self.system.take() {
			s.stop();
		}
	}
}

fn module_executor(clients: &mut HashMap<String, &mut Clients>, session_id: String) {
	//define a handy data struct, makes function signatures easier to read. any function "f" accepts Data
	let call_data = Data { clients, session_id: &session_id };
	//choose between different functions and call them (in this example only f exists)
	f(call_data);
}

fn f(call_data: Data)
{
	let clients;
	if !call_data.clients.contains_key(call_data.session_id) {
		clients = Clients::new();
		call_data.clients.insert(call_data.session_id.to_string(), &mut clients);
	}

	//get a client by session id
	let my_clients = &mut call_data.clients.get_mut(call_data.session_id).unwrap();

	//1st change some value
	{
		let system = &mut my_clients.system.as_mut().unwrap();
		system.ip = "123".to_string();
	}

	//then downgrade
	my_clients.downgrade_system();

	//and upgrade
	let system_client = SystemClient::new(
		call_data.session_id.to_string(),
		"67890".to_string(),
	);

	my_clients.upgrade_system(system_client);
}

Here's the complete output.

error[E0597]: `clients` does not live long enough                        ] 0/1: testt                                                                                              
   --> src/main.rs:79:67
    |
79  |         call_data.clients.insert(call_data.session_id.to_string(), &mut clients);
    |                                                                         ^^^^^^^ borrowed value does not live long enough
...
101 | }
    | - borrowed value only lives until here
    |
note: borrowed value must be valid for the anonymous lifetime #2 defined on the function body at 74:1...
   --> src/main.rs:74:1
    |
74  | / fn f(call_data: Data)
75  | | {
76  | |     let clients;
77  | |     if !call_data.clients.contains_key(call_data.session_id) {
...   |
100 | |     my_clients.upgrade_system(system_client);
101 | | }
    | |_^

Thanks for any suggestions!

References are not pointers, but temporary locks on data.

You don't have any place where you store clients. In the hash map you only borrow them temporarily, but there's no owned storage for them except a temporary variable inside a function.

Reference can't exit without pointing to an owned value.

Try not using &mut in any data structures, and store owned objects instead. If you need pointers, use Box or Rc pointers.

2 Likes

OK, I removed the &mut Clients, it works now.
Thanks!