2

I want to access data that is protected by a RwLock. Unfortunately, I am forced to return a reference.

Background: I am using rust-qt-binding-generator with QtQuick and I want to visualize data that is being received on the rust side asynchronously. The data comes as Vec, and consequently, I want to use QByteArray on the Qt side. rust-qt-binding-generator offers this data type.

My problem is that the generated trait requires a getter function with the following signature:

fn channels(&self) -> &[u8]

Code:

#![allow(unused_imports)]
#![allow(unused_variables)]
#![allow(dead_code)]
use interface::*;
use std::sync::{Arc, RwLock, RwLockWriteGuard};
use std::time::Duration;
use std::thread;

pub struct RustController {
    emit: RustControllerEmitter,
    ch_vals: Vec<u8>,
    channels: Arc<RwLock<Vec<u8>>>
}

impl RustControllerTrait for RustController {
    fn new(emit: RustControllerEmitter) -> RustController {
        let mut ret = RustController {
            emit: emit.clone(),
            ch_vals: vec!(),
            channels: Arc::new(RwLock::new(create_empty_vector())),
        };

        update_thread(emit, ret.channels.clone());
        ret
    }
    fn emit(&self) -> &RustControllerEmitter {
        &self.emit
    }
    fn channels(&self) -> &[u8] {
        // *((*self.channels).read().unwrap()).clone()
        // does not work of course (and nothing else I tried obviously)
        // because I can only get the value as a temporary one. writing
        // them into self.ch_vals would solve the problem, but I am not
        // allowed to get a &mut self

        //&self.ch_vals     // <-- this would work, but does not contain
                            //     the values generated in the thread.
    }
}

fn create_empty_vector() -> Vec<u8> {
    let mut ret: Vec<u8> = Vec::with_capacity(512);

    for i in 0..512 {
        ret.push((i/2) as u8);
    }

    ret
}

fn update_thread(
    emit: RustControllerEmitter,
    channels: Arc<RwLock<Vec<u8>>>,
) {
    thread::spawn(move || {
        loop {
            {
                let mut w: RwLockWriteGuard<Vec<u8>> = (*channels).write().unwrap();
                (*w)[0] += 1;
                // changing values in the mutex is no problem, but no
                // reference to the RustController here to store value
            }

            emit.channels_changed();
            thread::sleep(Duration::from_secs(1));
        }
    });
}

If you want to run it yourself, here is my bindings.json:

{
    "cppFile": "src/Bindings.cpp",
    "rust": {
        "dir": "rust",
        "interfaceModule": "interface",
        "implementationModule": "implementation"
    },
    "objects": {
        "RustController": {
            "type": "Object",
            "properties": {
                "channels": {
                    "type": "QByteArray"
                }
            }
        }
    }
}

With this setup I am able to transfer an i32 without problems, but Vec does not work because of the getter. My understanding of the problem is that I am unable to store the values generated in the extra thread in a place which can be referenced in the getter (and lives long enough, ideally as long as the RustController in use).

  • 1
    https://stackoverflow.com/questions/29401626/how-do-i-return-a-reference-to-something-inside-a-refcell-without-breaking-encap is essentially similar. I'm pretty sure that `fn channels(&self) -> &[u8]` is not a safe API for what you want `channels` to do. Either the binding generator is wrong, and `channels` should have a different signature, or the binding generator is right, and there *is no safe way* to write `channels` such that it reads from the shared memory. – trent Jul 21 '18 at 13:00
  • Actually, there is at least one way to write `channels` that is safe: just leak the whole `Vec`. But assuming you don't want to do that, and you're not willing or able to change the API, you're in a bind. – trent Jul 21 '18 at 13:02
  • Thank you for the link, I took a look, but none of the three solutions actually solve my problem, as I always have to change the function interface. Yep, leaking the whole Vec sounds not good to me as well. For now, I changed the generated sources to give me a mutable reference and I safe the values from the mutex into my RustController. I am going to contact the developer whether this is intended, or maybe he had a different solution on his mind, or maybe he just did not think of a usecase like this. – conducting_dmx Jul 22 '18 at 12:25
  • I ran into a similar question (want to return a ref to the data protected by a RwLock) here: https://stackoverflow.com/questions/51183343/how-to-use-interior-mutability-pattern-to-obtain-a-reference-of-a-struct-and-us , while my accepted answer does not really solve my issue, but it might be useful for you. – Xu Chen Jul 24 '18 at 07:00
  • 1
    So, though the problem on the rust side remains, this specific issue concerning the binding generator is resolved. Instead of having the channels property, I use an extra function get_channels() which returns a Vec instead of a slice. Getting a copy of the vector inside the RwLock is not a problem. Additionally, there is a property wich can be changed to emit a signal for the QML side. The field ch_vals of the controller becomes obsolete. – conducting_dmx Aug 09 '18 at 19:03

0 Answers0