Unable to dereference TcpStream object

Hi,

I am trying to access a TcpStream object by passing a mutable reference and then dereferencing it, but the following error comes up:

cannot move out of *tcp_ph which is behind a mutable reference

move occurs because *tcp_ph has type std::net::TcpStream, which does not implement the Copy trait

help: consider borrowing here: &*tcp_ph

Below is the code:

extern crate clap;
use bytes::Bytes;
use std::convert::TryInto;
use std::io::{self, Write};
use std::net::Shutdown;
use std::net::TcpStream;
use std::time::SystemTime;
use hdrhistogram::Histogram;
use std::io::Read;
use std::io::Seek;
use std::io::SeekFrom;

struct LoopStruct {
    tcp: TcpStream,
    current_pointer: usize,
    hist: Histogram<u64>,
}

fn main() -> io::Result<()> {
    
    let addr = String::from("127.0.0.1:8000");
    let loop_max = 100;
    
    let xnow = SystemTime::now();
    // tcp and tcp2 are the connection pool
    
    let mut tcp = TcpStream::connect(&addr);
    let tcp2 = TcpStream::connect(&addr);

    let mut flag = 1;
    let mut tcp_timer_x = SystemTime::now();
    match tcp {
        Err(e) => {
            println!("Cannot connect {:?}", e);    
        }
        Ok(ref mut tcp_ph) => {
            let current_pointer: usize = 0;
            let hist: Histogram<u64> = Histogram::<u64>::new(2).unwrap();
            //below line doesn't doesn't work
            let tcp = *tcp_ph;
            let mut t1 = LoopStruct {
                tcp,
                current_pointer,
                hist,
            };
            let mut loop_count = 1;
            loop {
                let mut i = 0;
                loop{
                        let tcp_timer_y: u64 = tcp_timer_x.elapsed().unwrap().as_secs().try_into().unwrap();
                        let mut count =1;
                        if tcp_timer_y > 5{
                            println!("Switching now -------- $$$$$$$$$$$$$");
                            t1.tcp.shutdown(Shutdown::Both);
                            println!("shutdown old conn -------- $$$$$$$$$$$$$");
                            if flag ==1 {
                                //t1.tcp = *(&mut tcp2).unwrap();
                                t1.tcp = tcp2.unwrap();
                                //should reconnect here
                                flag=2;
                            }
                            else {
                                t1.tcp = tcp;
                                flag=1;
                            }
                            //t1.tcp = curr_tcp_conn;
                            count= count+1;
                            println!("started new conn -------- $$$$$$$$$$$$$ {:?}", count);
                            tcp_timer_x = SystemTime::now();
                            } 
                    }
                }
                loop_count += 1;
                if loop_count > loop_max {
                let xnow1: u64 = xnow.elapsed().unwrap().as_millis().try_into().unwrap();
                    t1.tcp
                        .shutdown(Shutdown::Both)
                        .expect("shutdown call failed");
                }
        }
    }
    Ok(())
}

You cannot get an owned value out of a &mut or & reference —unless you respectively use use mem::replace or Copy. And you had a &mut TcpStream because of the ref mut pattern. Since your LoopStruct wants ownership of the TcpStream, you'll have to pass stuff around in an owned fashion.

  • Given the pattern, you might have wanted to just have the LoopStruct refer to / point to / borrow the TcpStream, in which case you'd have needed to do:

    struct LoopStruct<'tcp> {
        tcp: &'tcp mut TcpStream,
        …
    

    which, I'd say, seemed like the best starting point for your code…

… except you then keep juggling around —with two nested infinite loops!– and I don't really know what your objective really was. Anyways, at that point the borrows would interleave, which is not something possible with exclusive borrows (&mut). You'd need to use instead shareable / copyable borrows (&), with runtime-checked critical zones / areas of exclusive access: TcpStream -> RefCell<TcpStream>, and &mut TcpStream -> &RefCell<TcpStream> (+ .tcp.borrow_mut() when wanting to interact with the TcpStream).

Since that would have been a big code overhaul, I went with another simpler pattern: own everything, and pass stuff in —and back!– around. Since the drop / ownership checker gets (legitimately) confused by this pattern, delegate to runtime checks by using Options and .take()s:

use ::{
    bytes::Bytes,
    hdrhistogram::Histogram,
    std::{
        convert::TryInto,
        io::{self,
            Read, Seek, SeekFrom, Write,
        },
+       mem,
        net::{Shutdown, TcpStream},
        time::SystemTime,
    },
};

struct LoopStruct {
    tcp: TcpStream,
    current_pointer: usize,
    hist: Histogram<u64>,
}

fn main ()
  -> io::Result<()>
{
    let addr = String::from("127.0.0.1:8000");
    let loop_max = 100;

    let xnow = SystemTime::now();
    // tcp and tcp2 are the connection pool

    let tcp = TcpStream::connect(&addr);
    let tcp2 = TcpStream::connect(&addr);

    let mut flag = 1;
    let mut tcp_timer_x = SystemTime::now();
    match tcp {
        Err(e) => {
            println!("Cannot connect {:?}", e);
        },
-       Ok(ref mut tcp_ph) => {
+       Ok(mut tcp) => { /* Take ownership of the `Ok` contents */
+          let tcp2 = tcp2.unwrap(); // To keep `tcp` and `tcp2` at the same level, let's panic on `tcp2` connection failure (you were gonna do that anyways later on)

+           // The `Option` pattern: you have a weird pattern of juggling things around,
+           // which "confuses" Rust. Opt out of compile-time checks for runtime guarantees,
+           // since the runtime errors may help you figure out what's wrong with your pattern.
+           // Once you fix it, try to move away from this `Option` pattern and go wrap to
+           // classical compiler-checked ownership.
+           let mut tcp = Some(tcp);
+           let mut tcp2 = Some(tcp2);
            let current_pointer: usize = 0;
            let hist: Histogram<u64> = Histogram::<u64>::new(2).unwrap();
-           // below line doesn't doesn't work
-           let tcp = *tcp_ph;
            let mut t1 = LoopStruct {
-               tcp,
+               tcp: tcp.take().unwrap(),
                current_pointer,
                hist,
            };
            let mut loop_count = 1;
            loop {
                let mut i = 0;
                loop {
                    let tcp_timer_y: u64 =
                        tcp_timer_x
                            .elapsed()
                            .unwrap()
                            .as_secs()
                            .try_into()
                            .unwrap()
                    ;
                    let mut count = 1;
                    if tcp_timer_y > 5 {
                        println!("Switching now -------- $$$$$$$$$$$$$");
                        t1.tcp.shutdown(Shutdown::Both);
                        println!("shutdown old conn -------- $$$$$$$$$$$$$");
                        if flag == 1 {
                            //t1.tcp = *(&mut tcp2).unwrap();
-                           t1.tcp = tcp2;
+                           // Set `t1.tcp` to `tcp2`, but in doing so get back the `tcp1` it `took` from us
+                           tcp = Some(mem::replace(&mut t1.tcp, tcp2.take().unwrap()));
                            //should reconnect here
                            flag = 2;
                        } else {
-                           t1.tcp = tcp;
+                           // Symmetrical situation
+                           tcp2 = Some(mem::replace(&mut t1.tcp, tcp.take().unwrap()));
                            flag = 1;
                        }
                        //t1.tcp = curr_tcp_conn;
                        count = count + 1;
                        println!("started new conn -------- $$$$$$$$$$$$$ {:?}", count);
                        tcp_timer_x = SystemTime::now();
                    }
                }
            }
            loop_count += 1;
            if loop_count > loop_max {
                let xnow1: u64 =
                    xnow.elapsed()
                        .unwrap()
                        .as_millis()
                        .try_into()
                        .unwrap()
                ;
                t1  .tcp
                    .shutdown(Shutdown::Both)
                    .expect("shutdown call failed")
                ;
            }
        },
    }
    Ok(())
}