Accessing structure members at two different places


#1

Hello,

Below is a sample example code which has a point structure. Run function spawns a socket and listens to the stream of data using stream.map. While there is an tokio based http server listening for any incoming connections.

The stream.map contains data that needs to be passed to http server. To Accomplish the task, I am using this point structure.

use errors::*;
quick_main!(run);

#[derive(Clone, Debug)]
struct Point {
	x: u32,
	y: u32,
	result: String,
	status: bool
}

impl Point {
	fn update_results(&mut self, resultp: String){
	self.result = resultp;
	println!("Update Struct : {:?}", self.result);
	}
	
	fn update_status(&mut self, x: bool) {
	self.status = x;
	}
}


struct ExampleServer <'a> {
	wpa_cmd: mpsc::Sender<String>,
	wpa_db: &'a Point,
}

impl<'a> ExampleServer<'a> {
	fn new(wpa_cmd: mpsc::Sender<String>, P0: &'a Point) -> ExampleServer {
		ExampleServer {
			wpa_cmd: wpa_cmd,
			wpa_db: P0,
		}
	}
}

impl <'a> Service for ExampleServer <'a>{
	type Request = Request;
	type Response = Response;
	type Error = hyper::Error;
	type Future = Box<Future<Item=Self::Response, Error=Self::Error>>;

	fn call(&self, req: Request) -> Self::Future {
	match (req.method(), req.path()) {
			(&Method::Get, "/") => {
			println!("{:?}", self.wpa_db.result); // need the stream.map data here
		 Box::new(futures::future::ok(response))
			},        
		}
	}
}

fn run() -> Result<()> {

	let (cmd_send, cmd_recv) = mpsc::channel::<String>(5); // extra buffer
	
	let mut p0 = Point { x: 5, y: 10, result: String::from("Hello World"), status: false};
	let p2 = p0.clone();

	let http_server = {
		let wpa_data = wpa_data.clone();
		let cmd_send = cmd_send.clone();
		

		Http::new().bind(
			&addr,
			move || Ok(ExampleServer::new(cmd_send.clone(), &p0))
		)?
	};
	
	
	let wpa_data = wpa_data.clone();
	let sock = UnixDatagram::bind(SCAN_AND_CONNECT_PATH, &handle).unwrap().framed(StringDatagramCodec);
	let f = sock.send("TEST".to_string()).and_then(move |sock| {
		let (sink, stream) = sock.split();
		
		let mapped_stream = stream.map(move |msg| {
			let wpa_data = wpa_data.clone();
			let mut ret = "".to_string();
			if msg == "OK\n" {
			} else if msg == "<3>RECEIVED DATA " {
				println!("Recvd msg notification {}", msg);
				ret = "TEST_RESULT".to_string();
				p0.update_results(ret.clone()); // NEED THIS DATA To be available in HTTP
			} 
			ret
		}).select(cmd_recv.map_err(|()| {
			use std::io::{Error, ErrorKind};
			Error::new(ErrorKind::Other, "end of stream")
		}));
		sink.send_all(mapped_stream)
	});
	
	handle.spawn(f.map(|_| ()).map_err(|_| ()));
	http_server.run()?;
	
	Ok(())
}

If I pass p0 directly to the new of http server, I get below error.

If I pass p2 to the http server, since it is a clone, any update to p0 structure would not be reflected in the httpserver.
i.e., p0.update_results(ret.clone()); would not be reflected in the self.wpa_db structure.

How could we make this structure accessible in the http server?

Thanks a Ton in Advance.


#2

The simplest would be to wrap your point in a Arc<Mutex<Point>> and share it via Arc::clone values. Access to the point itself would go through the mutex.


#3

Thanks vitalyd for pointing to Arc mutex stuff. I tried using it as below :

let p0 = Arc::new(Mutex::new(Point { x: 5, y: 10, result: String::from("Hello World"), status: false}));
let p2 = p0.clone();

    let http_server = {
    let wpa_data = wpa_data.clone();
    let cmd_send = cmd_send.clone();
    

    Http::new().bind(
        &addr,
        move || Ok(ExampleServer::new(wpa_data.clone(), cmd_send.clone(), &p2))
    )?
};

/// INSIDE stream.map
  let mut tls = Arc::try_unwrap(p0).unwrap();
           let mut res = tls.lock().unwrap();
           res.update_results(sRet);

/// INSIDE the http server 
	let mut tls = Arc::try_unwrap(*self.wpa_db).unwrap();
			let mut res = tls.lock().unwrap();
			println!("{:?}", res.result);

I am getting these below errors, I tried also using Arc::new for the structure as well. I am facing similar errors ,is there something wrong I am doing
here? Any help would be of appreciated.

  Compiling tokio-server v0.2.0 (file:///home/vinay245/projects/Patrick/tokio-server)
  error[E0507]: cannot move out of borrowed content
 --> src/main.rs:105:35
  |
 105 | 				let mut tls = Arc::try_unwrap(*self.wpa_db).unwrap();
   | 				                              ^^^^^^^^^^^^ cannot move out of borrowed content

   error[E0507]: cannot move out of captured outer variable in an `FnMut` closure
  --> src/main.rs:246:46
  |
  189 |     let p0 = Arc::new(Mutex::new(Point { x: 5, y: 10, result: String::from("Hello World"), status: false}));
  |         -- captured outer variable
  ...
  246 |                let mut tls = Arc::try_unwrap(p0).unwrap();
    |                                              ^^ cannot move out of captured outer variable in an `FnMut` closure

      error: aborting due to 2 previous errors

     error: Could not compile `tokio-server`.

    To learn more, run the command again with --verbose.

#4

I used the example code provided here : https://hoverbear.org/2017/03/01/the-future-with-futures/
I was able to accomplish the needed. Thanks a ton for the help.