简体   繁体   中英

Rust multiple borrowing struct

I am new to Rust and trying to implement a simple TCP-Server that sends me a live image. Therefore I am using this crate https://crates.io/crates/tinybmp for the RawBmp -type. Now I am struggling a bit to understand some of Rusts functionality. Or let's say I am still trying to figure out how to accomplish this in the 'Rust way'.

pub struct TCPBasePackage {
    haspayload: bool,
    pkgbytes: Vec<u8>,
}

impl TCPBasePackage {
    pub const CMD: i32 = 0;

    pub fn new() -> TCPBasePackage {
        TCPBasePackage { haspayload: false, pkgbytes: Vec::new()}
    }
}

impl TCPPackage for TCPBasePackage {
    fn serialize(&mut self) -> &[u8] {
        self.pkgbytes.append(&mut self.get_command().to_be_bytes().to_vec());        
        self.pkgbytes.append(&mut [u8::from(self.haspayload)].to_vec());
        self.pkgbytes.as_slice()
    }

    fn read_from_stream(&mut self, mut stream: &TcpStream) -> Vec<u8> {
        let mut vec: Vec<u8> = Vec::new();
        let mut buf_pkg: [u8; 5] = [0; 5];
        stream.read_exact(&mut buf_pkg).unwrap();
        vec.append(&mut buf_pkg.to_vec());
        self.pkgbytes = vec.to_vec();
        return self.pkgbytes.clone();
    }

    fn deserialize(&mut self, buff: &[u8]) -> i32 {
        self.haspayload = buff[4] != 0;
        return 5;
    }

    fn get_command(&self) -> i32 {
        TCPBasePackage::CMD
    }
}

pub struct MsgLiveImage<'a> {
    tcppackage: TCPBasePackage,
    imgbytes: Vec::<u8>,
    img: RawBmp<'a>,
}

impl<'a> MsgLiveImage<'a> {
    pub const CMD: i32 = 2;

    pub fn new() -> MsgLiveImage<'static> {
        MsgLiveImage { 
tcppackage: TCPBasePackage { haspayload: false, pkgbytes: Vec::new() },
            imgbytes: Vec::new(),
            img: RawBmp::from_slice(&[]).unwrap()
        }
    }

    pub fn set_image_data(&'a mut self, data: Vec::<u8>) {
        self.imgbytes = data;
        self.img = RawBmp::from_slice(&self.imgbytes.as_slice()).unwrap();
    }

}

In some function of my application I want to do this:

fn write_response_to_stream(
    mut request: &Box<dyn mytcp::TCPPackage>,
    mut stream: &TcpStream,
    raspicam: &Arc<Mutex<camserv::CamServ>>,
) -> Result<Box<dyn mytcp::TCPPackage>, &'static str> {
    match request.get_command() {
        mytcp::MsgLiveImage::CMD => {
            let mut pkg = mytcp::MsgLiveImage::new();
            {
                {
                    let tmpcam = raspicam.lock().unwrap(); // lock the camera server 
                    let tmpbuff = tmpcam.imgbuff.lock().unwrap(); // lock the buffer of last image
                    pkg.set_image_data((*tmpbuff).clone()); // make copy of my image and transfer ownership of that copy to the package struct
                }
                {
                    let sbuff = pkg.serialize();
                    stream.write(&sbuff);
                }
                stream.flush();
            }
            Ok(Box::new(pkg)) // return the response package
        }
        _ => Err("Unknown request package"),
    }
}

But the problem is, that the compiler is complaining about multiple borrowing the pkg -variable. As well as moving the pkg out of scope (which i learned should be possible by wrapping it in a Box . Can someone explain me why the first mutable borrow still lasts even after the method returns? How can i achieve to call multiple methods on my struct without getting these borrowing conflicts?

Errors from rustc:

error[E0499]: cannot borrow `pkg` as mutable more than once at a time
  --> src/raspiserv/mod.rs:90:33
   |
87 |                     pkg.set_image_data((*tmpbuff).clone());
   |                     -------------------------------------- first mutable borrow occurs here
...
90 |                     let sbuff = pkg.serialize();
   |                                 ^^^^^^^^^^^^^^^ second mutable borrow occurs here
...
95 |             Ok(Box::new(pkg))
   |             ----------------- returning this value requires that `pkg` is borrowed for `'static`

error[E0515]: cannot return value referencing local variable `pkg`
  --> src/raspiserv/mod.rs:95:13
   |
87 |                     pkg.set_image_data((*tmpbuff).clone());
   |                     -------------------------------------- `pkg` is borrowed here
...
95 |             Ok(Box::new(pkg))
   |             ^^^^^^^^^^^^^^^^^ returns a value referencing data owned by the current function

error[E0505]: cannot move out of `pkg` because it is borrowed
  --> src/raspiserv/mod.rs:95:25
   |
87 |                     pkg.set_image_data((*tmpbuff).clone());
   |                     -------------------------------------- borrow of `pkg` occurs here
...
95 |             Ok(Box::new(pkg))
   |             ------------^^^--
   |             |           |
   |             |           move out of `pkg` occurs here
   |             returning this value requires that `pkg` is borrowed for `'static`

The problem is with your set_image_data

    pub fn set_image_data(&'a mut self, data: Vec::<u8>) {
        self.imgbytes = data;
        self.img = RawBmp::from_slice(&self.imgbytes.as_slice()).unwrap();
    }

you borrow self for the lifetime of 'a which is as long as the RawBmp inside of it is valid. Thus the borrow lasts as long as the struct.

What you have is a self referential struct see this question on them for suggestions how to reslove this.

You can probably just drop the img from MsgLiveImage and replace it with a method:

impl MsgLiveImage {
    pub fn as_img<'a>(&'a self) -> Result<RawBmp<'a>, ParseError> {
        RawBmp::from_slice(&self.imgbytes.as_slice())
    }
}

Might even be able to omit the lifetimes from that.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM