Closure for thread borrows object

Hi!

use gtk::{Builder, Button};
use gtk::prelude::*;
use rodio::{Decoder};
use std::io::BufReader;
use std::fs::File;

pub struct Pad {
    id: i32,
    audio_source: Option<Decoder<BufReader<File>>>,
    btn: Button,
}

impl Pad {
    pub fn new(id: i32, btn: Button) -> Pad {
        let pad = Pad {id: id, btn: btn, audio_source: None};
        btn.connect_clicked(|_| (&pad).on_click());
        pad
    }

    fn on_click(&self) {
        println!("{}", self.id);
    }
}

pub fn get_btn_by_id(id: i32, builder: &Builder) -> Option<Button> {
        builder.object(format!("pad-{}", id).as_str())
}
use gtk::prelude::*;
use gtk::{
    Application,
    ApplicationWindow,
    Button,
    Builder,
};

pub mod pads;

const APP_ID: &str = "org.reo.rdmachine";
const UI_CONTENT: &str = include_str!("ui/rdmachine.ui");

fn main() {
    let app = Application::new(
        Some(APP_ID),
        Default::default(),
    );

    app.connect_activate(build_ui);

    app.run();
}

fn build_ui(app: &Application) {
    let builder = Builder::from_string(UI_CONTENT);
    let mut padvec: Vec<pads::Pad> = vec![];

    for i in 1..17 {
        padvec.push(pads::Pad::new(
            (i+1).try_into().unwrap(),
            pads::get_btn_by_id(i, &builder).expect("Couldn't get button."),
        ));
    }

    let window: ApplicationWindow = builder.object("main-window").expect("Couldn't get window.");
    window.set_application(Some(app));
    window.show();
}

In gtk buttons, we have a function (connect_clicked) that takes another function and calls that function when the button is pressed. There are multiple buttons (I'll call them pads) in my gtk app, they are represented as Pads. Each pad has its own on_click handler and those handlers print out the ID of that pad (or do some sort of stuff with the pad object). In the new method I create a new Pad with given arguments and connect the created pad's on_click handler to the given gtk button. But the connect_clicked function's closure takes ownership of the pad I created.

I need ownership in both places. What should I do here?

Could you share ownership by wrapping the builder in an Arc?

use gtk::{Builder, Button};
use gtk::prelude::*;
use rodio::{Decoder};
use std::io::BufReader;
use std::fs::File;
use std::sync::Arc;

pub struct Pad {
    id: i32,
    audio_source: Option<Decoder<BufReader<File>>>,
    btn: Button,
}

impl Pad {
    pub fn new(id: i32, btn: Button) -> Arc<Pad> {
        let pad = Arc::new(Pad {id: id, btn: btn, audio_source: None});
        let pad2 = Arc::clone(&pad);
        btn.connect_clicked(move |_| pad2.on_click());
        Arc::clone(&pad)
    }

    fn on_click(&self) {
        println!("{}", self.id);
    }
}

pub fn get_btn_by_id(id: i32, builder: &Builder) -> Option<Button> {
        builder.object(format!("pad-{}", id).as_str())
}
use gtk::prelude::*;
use gtk::{
    Application,
    ApplicationWindow,
    Button,
    Builder,
};

pub mod pads;

const APP_ID: &str = "org.reo.rdmachine";
const UI_CONTENT: &str = include_str!("ui/rdmachine.ui");

fn main() {
    let app = Application::new(
        Some(APP_ID),
        Default::default(),
    );

    app.connect_activate(build_ui);

    app.run();
}

fn build_ui(app: &Application) {
    let builder = Builder::from_string(UI_CONTENT);
    let mut padvec: Vec<std::sync::Arc<pads::Pad>> = vec![];
    let mut buttons: Vec<Button> = vec![];

    //for i in 1..17 {
    //    buttons.push(pads::get_btn_by_id(i, &builder).expect("Couldn't get button."));
    //}

    for i in 1..17 {
        padvec.push(pads::Pad::new(
            (i+1).try_into().unwrap(),
            pads::get_btn_by_id(i, &builder).expect("Couldn't get button."),
        ));
    }

    let window: ApplicationWindow = builder.object("main-window").expect("Couldn't get window.");
    window.set_application(Some(app));
    window.show();
}

I've tried something like this, the error:

error[E0382]: borrow of moved value: `btn`
  --> src/pads.rs:18:9
   |
15 |     pub fn new(id: i32, btn: Button) -> Arc<Pad> {
   |                         --- move occurs because `btn` has type `gtk4::Button`, which does not implement the `Copy` trait
16 |         let pad = Arc::new(Pad {id: id, btn: btn, audio_source: None});
   |                                              --- value moved here
17 |         let pad2 = Arc::clone(&pad);
18 |         btn.connect_clicked(move |_| pad2.on_click());
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ value borrowed here after move

I've no idea about what I am doing.

You moved btn into pad, so the btn variable is now empty. Access it via its new location pad.btn instead.

pub fn new(id: i32, btn: Button) -> Arc<Pad> {
    let pad = Arc::new(Pad {id: id, btn: btn, audio_source: None});
    let pad2 = Arc::clone(&pad);
    pad.btn.connect_clicked(move |_| pad2.on_click());
    pad
}
3 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.