How to access state and template children in callback in GTK-rs - rust

The tutorials and examples for gtk-rs are honestly really incomplete and spotty, so I'm trying to piece together how to modify the application's state, as well as the state of some of the child elements, inside a button callback. So, in brief, I have:
// ...
mod imp {
pub struct Window {
#[template_child]
pub headerbar: TemplateChild<gtk::HeaderBar>,
#[template_child]
pub open_button: TemplateChild<gtk::Button>,
// Internal state
pub state: Rc<RefCell<ScribeDownWindowState>>,
}
#[derive(Default)]
pub struct ScribeDownWindowState {
pub project_path: Option<String>,
}
}
In the ObjectImpl for this struct, I have the constructed method, which calls the parent constructed method, then calls setup_callbacks on the parent object, which is the Window type that actually is part of the GTK inheritance hierarchy:
mod imp;
glib::wrapper! {
pub struct Window(ObjectSubclass<imp::Window>)
#extends gtk::ApplicationWindow, gtk::Window, gtk::Widget,
#implements gio::ActionGroup, gio::ActionMap;
}
impl Window {
pub fn new<P: glib::IsA<gtk::Application>>(app: &P) -> Self {
glib::Object::new(&[("application", app)]).expect("Failed to create ScribeDownWindow")
}
fn setup_callbacks(&self) {
let state = self.imp().state;
let headerbar = Rc::new(&self.imp().headerbar);
self.imp().open_button
.connect_clicked(clone!(#strong state, #strong headerbar => move |_| {
let s = state.borrow_mut();
s.project_path = Some("fuck".to_string());
headerbar.set_subtitle(Some("fuck"));
}))
}
}
I need to access both the state and headerbar properties of the imp::Window struct, and modify the project_path property of state and call set_subtitle on the headerbar. I've tried all sorts of variations of this, using all combinations of variables and Rcs and RefCells and I just cannot seem to get past this error (or some permutation of it):
error[E0759]: `self` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement
--> src/scribedown_window/mod.rs:22:39
|
20 | fn setup_callbacks(&self) {
| ----- this data with an anonymous lifetime `'_`...
21 | let state = self.imp().state;
22 | let headerbar = Rc::new(&self.imp().headerbar);
| ---- ^^^
| |
| ...is captured here...
23 | self.imp().open_button.connect_clicked(
| --------------- ...and is required to live as long as `'static` here
There has to be a way to get what I need done done, if you couldn't modify any other interface objects inside a button click callback your UI would be seriously hindered, but I don't see how.

The solution to this problem was to create a struct to hold both the UI state and the application state, like so:
pub struct App {
pub window: crate::scribedown_window::Window,
pub state: State,
pub document_list_model: Option<document_list::Model>,
}
With this struct in hand, you can wrap it in an Rc<RefCell<T>> so that other threads and scopes can access it (just not thread-safely/at the same time, you need a Mutex or Arc for that):
let scribedown = Rc::new(RefCell::new(app::App {
window: win,
state: app::State {
project: None,
open_files: vec![],
},
document_list_model: None,
}));
Now, you can just pass a reference counted pointer to this central state holder to all the callbacks you want, and the callbacks themselves will keep the state alive and keep access to it, while also enforcing a crash if multiple callbacks try to modify the RefCell at the same time. Note that for this to work, all the methods to set up the application's UI callbacks need to be passed the reference counted state variable, scribedown, so they can't be methods of the App struct taking &self, since that would be useless and borrow it. They can be static methods though:
app::App::connect_all(scribedown.clone());
Then, to wire up the callbacks, each callback needs its own pointer to the state to use, and on top of that, since you can't move a cloned reference counted pointer out of the enclosing scope, and you don't want to move the original reference counted pointer out, you need to create an outer RCP to then use to clone the actual RCP for the callback. That ends up looking like this:
// NOTE: the outer pointers to `sd`, formatted like `sd_for_*`, are
// done in order to prevent the callback from borrowing the original
// pointer when it creates its own pointer, which we need to keep free
// to continue making more pointers. This happens because using
// something inside a `move` callback borrows it.
// Connect open button callback
let sd_for_button = sd.clone();
{
let osd = sd.borrow();
let button = &osd.window.imp().open_button;
button.connect_clicked(move |_| {
// Launch dialog in new thread so it doesn't hang this one
gtk::glib::MainContext::default()
.spawn_local(Self::open_project_dialog(Rc::clone(&sd_for_button)));
});
}
I'm not sure this is the "official" or idiomatic solution to this, but I looked at the source code for Fractal (a Matrix messenger client written in Rust with GTK-rs), and they seemed to be using a similar solution.

You can clone self itself for the closure:
fn setup_callbacks(&self) {
self.imp().open_button
.connect_clicked(clone!(#weak self as this => move |_| {
this.imp().state.borrot_mut().project_path = Some("nice".to_string());
this.imp().headerbar.set_subtitle(Some("nice"));
}));
}

Related

Rust member functions as multithreaded mutable callbacks in real-time safe code

I am looking to use a member function of a struct as a mutable callback. The standard way I would do this is by wrapping my struct instance in a mutex and then in an Arc. In this use case however, my callback is a real-time audio callback which needs to be real-time safe and is triggered on a dedicated thread. This means no locking can happen that might delay the callback from occurring.
My actual member callback method must take a mutable reference to the struct (I think this is where things fall apart). The struct does contain members but these can all be atomic variables.
Here is an example of the kind of thing I'm looking for:
use std::sync::Arc;
use clap::error;
use cpal::{traits::{HostTrait, DeviceTrait}, StreamConfig, OutputCallbackInfo, StreamError};
use std::sync::atomic::AtomicI16;
pub struct AudioProcessor {
var: AtomicI16,
}
impl AudioProcessor {
fn audio_block_f32(&mut self, audio: &mut [f32], _info: &OutputCallbackInfo) {
}
fn audio_error(&mut self, error: StreamError) {
}
}
fn initAudio()
{
let out_dev = cpal::default_host().default_output_device().expect("No available output device found");
let mut supported_configs_range = out_dev.supported_output_configs().expect("Could not obtain device configs");
let config = supported_configs_range.next().expect("No available configs").with_max_sample_rate();
let proc = Arc::new(AudioProcessor{var: AtomicI16::new(5)});
let audio_callback_instance = proc.clone();
let error_callback_instance = proc.clone();
out_dev.build_output_stream(&StreamConfig::from(config), move |audio: &mut [f32], info: &OutputCallbackInfo| audio_callback_instance.audio_block_f32(audio, info), move |stream_error| error_callback_instance.clone().audio_error(stream_error));
}
Currently this code fails in the build_output_stream() function with this error message:
cannot borrow data in an Arc as mutable
trait DerefMut is required to modify through a dereference, but it is not implemented for Arc<AudioProcessor>
Are there any standard ways of dealing with problems like this?

How to produce static references from append-only arena?

In my application (a compiler), I'd like to create data cyclic data structures of various kinds throughout my program's execution that all have the same lifetime (in my case, lasting until the end of compilation). In addition,
I don't need to worry about multi-threading
I only need to append information - no need to delete or garbage collect
I only need immutable references to my data
This seemed like a good use case for an Arena, but I saw that this would require passing the arena around to every function in my program, which seemed like a large overhead.
So instead I found a macro called thread_local! that I can use to define global data. Using this, I thought I might be able to define a custom type that wraps an index into the array, and implement Deref on that type:
use std::cell::RefCell;
enum Floop {
CaseA,
CaseB,
CaseC(FloopRef),
CaseD(FloopRef),
CaseE(Vec<FloopRef>),
}
thread_local! {
static FLOOP_ARRAY: RefCell<Vec<Box<Floop>>> = RefCell::new(Vec::new());
}
pub struct FloopRef(usize);
impl std::ops::Deref for FloopRef {
type Target = Floop;
fn deref(&self) -> &Self::Target {
return FLOOP_ARRAY.with(|floops| &floops.borrow()[self.0]);
}
}
pub fn main() {
// initialize some data
FLOOP_ARRAY.with(|floops| {
floops.borrow_mut().push(Box::new(Floop::CaseA));
let idx = floops.borrow_mut().len();
floops.borrow_mut().push(Box::new(Floop::CaseC(FloopRef(idx))));
});
}
Unfortunately I run into lifetime errors:
error: lifetime may not live long enough
--> src/main.rs:20:36
|
20 | return FLOOP_ARRAY.with(|floops| &floops.borrow()[self.0]);
| ------- ^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'2`
| | |
| | return type of closure is &'2 Box<Floop>
| has type `&'1 RefCell<Vec<Box<Floop>>>`
error[E0515]: cannot return value referencing temporary value
--> src/main.rs:20:36
|
20 | return FLOOP_ARRAY.with(|floops| &floops.borrow()[self.0]);
| ^---------------^^^^^^^^
| ||
| |temporary value created here
| returns a value referencing data owned by the current function
What I'd like to tell the compiler is that I promise I'm never going to remove entries from the Array and that I'm not going to share values across threads and that the array will last until the end of the program so that I can in essence just return a &'static reference to a Floop object. But Rust doesn't seem to be convinced this is safe.
Is there any kind of Rust helper library that would let me do something like this? Or are there safety holes even when I guarantee I only append / only use data with a single thread?
If you would have a reference, you could send the data to another thread, then watch it after it has been dropped because the creating thread was finished.
Even if you would solve this problem, this would still require unsafe code, as the compiler can't be convinced that growing the Vec won't invalidate existing references. This is true in this case since you're using Box, but the compiler cannot know that.
If you pinky promise to never touch the data after the creating thread has finished, you can use the following code. Note that this code is technically UB as when the Vec will grow, we will move all Boxes, and at least currently, moving a Box invalidates all references deriven from it:
enum Floop {
CaseA,
CaseB,
CaseC(&'static Floop),
CaseD(&'static Floop),
CaseE(Vec<&'static Floop>),
}
thread_local! {
static FLOOP_ARRAY: RefCell<Vec<Box<Floop>>> = RefCell::new(Vec::new());
}
fn alloc_floop(floop: Floop) -> &'static mut Floop {
FLOOP_ARRAY.with(|floops| {
let mut floops = floops.borrow_mut();
floops.push(Box::new(floop));
let floop = &mut **floops.last_mut().unwrap() as *mut Floop;
// SAFETY: We never access the data after it has been dropped, and we are
// the only who access this `Box` as we access a `Box` only immediately
// after pushing it.
unsafe { &mut *floop }
})
}
fn main() {
let floop_a = alloc_floop(Floop::CaseA);
let floop_b = alloc_floop(Floop::CaseC(floop_a));
}
A better solution would be something like a thread-safe arena that you can use in a static, but sadly, I found no crate that implements that.

Getting around Rust ownership problems when using state machine pattern

This question is about a specific pattern of ownership that may arise when implementing a state machine for a video game in Rust, where states can hold a reference to "global" borrowed context and where state machines own their states. I've tried to cut out as many details as I can while still motivating the problem, but it's a fairly large and tangled issue.
Here is the state trait:
pub trait AppState<'a> {
fn update(&mut self, Duration) -> Option<Box<AppState<'a> + 'a>>;
fn enter(&mut self, Box<AppState<'a> + 'a>);
//a number of other methods
}
I'm implementing states with a boxed trait object instead of an enum because I expect to have quite a lot of them. States return a Some(State) in their update method in order to cause their owning state machine to switch to a new state. I added a lifetime parameter because without it, the compiler was generating boxes with type: Box<AppState + 'static>, making the boxes useless because states contain mutable state.
Speaking of state machines, here it is:
pub struct StateMachine<'s> {
current_state: Box<AppState<'s> + 's>,
}
impl<'s> StateMachine<'s> {
pub fn switch_state(&'s mut self, new_state: Box<AppState<'s> + 's>) -> Box<AppState<'s> + 's> {
mem::replace(&mut self.current_state, new_state);
}
}
A state machine always has a valid state. By default, it starts with a Box<NullState>, which is a state that does nothing. I have omitted NullState for brevity. By itself, this seems to compile fine.
The InGame state is designed to implement a basic gameplay scenario:
type TexCreator = TextureCreator<WindowContext>;
pub struct InGame<'tc> {
app: AppControl,
tex_creator: &'tc TexCreator,
tileset: Tileset<'tc>,
}
impl<'tc> InGame<'tc> {
pub fn new(app: AppControl, tex_creator: &'tc TexCreator) -> InGame<'tc> {
// ... load tileset ...
InGame {
app,
tex_creator,
tileset,
}
}
}
This game depends on Rust SDL2. This particular set of bindings requires that textures be created by a TextureCreator, and that the textures not outlive their creator. Texture requires a lifetime parameter to ensure this. Tileset holds a texture and therefore exports this requirement. This means that I cannot store a TextureCreator within the state itself (though I'd like to), since a mutably-borrowed InGame could have texture creator moved out. Therefore, the texture creator is owned in main, where a reference to it is passed to when we create our main state:
fn main() {
let app_control = // ...
let tex_creator = // ...
let in_game = Box::new(states::InGame::new(app_control, &tex_creator));
let state_machine = states::StateMachine::new();
state_machine.switch_state(in_game);
}
I feel this program should be valid, because I have ensured that tex_creator outlives any possible state, and that state machine is the least long-lived variable. However, I get the following error:
error[E0597]: `state_machine` does not live long enough
--> src\main.rs:46:1
|
39 | state_machine.switch_state( in_game );
| ------------- borrow occurs here
...
46 | }
| ^ `state_machine` dropped here while still borrowed
|
= note: values in a scope are dropped in the opposite order they are created
This doesn't make sense to me, because state_machine is only borrowed by the method invocation, but the compiler is saying that it's still borrowed when the method is over. I wish it let me trace who the borrower in the error message--I don't understand why the borrow isn't returned when the method returns.
Essentially, I want the following:
That states be implemented by trait.
That states be owned by the state machine.
That states be able to contain references to arbitrary non-static data with lifetime greater than that of the state machine.
That when a state is swapped out, the old box be still valid so that it can be moved into the constructor of the new state. This will allow the new state to switch back to the preceding state without requiring it to be re-constructed.
That a state can signal a state change by returning a new state from 'update'. The old state must be able to construct this new state within itself.
Are these constraints possible to satisfy, and if so, how?
I apologize for the long-winded question and the likelihood that I've missed something obvious, as there are a number of decisions made in the implementation above where I'm not confident I understand the semantics of the lifetimes. I've tried to search for examples of this pattern online, but it seems a lot more complicated and constrained than the toy examples I've seen.
In StateMachine::switch_state, you don't want to use the 's lifetime on &mut self; 's represents the lifetime of resources borrowed by a state, not the lifetime of the state machine. Notice that by doing that, the type of self ends up with 's twice: the full type is &'s mut StateMachine<'s>; you only need to use 's on StateMachine, not on the reference.
In a mutable reference (&'a mut T), T is invariant, hence 's is invariant too. This means that the compiler considers that the state machine has the same lifetime as whatever it borrows. Therefore, after calling switch_state, the compiler considers that the state machine ends up borrowing itself.
In short, change &'s mut self to &mut self:
impl<'s> StateMachine<'s> {
pub fn switch_state(&mut self, new_state: Box<AppState<'s> + 's>) -> Box<AppState<'s> + 's> {
mem::replace(&mut self.current_state, new_state)
}
}
You also need to declare state_machine in main as mutable:
let mut state_machine = states::StateMachine::new();

Type must satisfy the static lifetime

I'm trying to increase the structure of a Rust and GTK-RS application, but I cannot figure out how to deal with event connections. I see that the problem is in wrong lifetime, but I do not really understand how it could be fixed.
#[derive(Debug)]
struct CreatingProfileUI {
window: gtk::MessageDialog,
profile_name_entry: gtk::Entry,
add_btn: gtk::Button,
cancel_btn: gtk::Button,
}
#[derive(Debug)]
struct UI {
window: gtk::Window,
// Header
url_entry: gtk::Entry,
open_btn: gtk::Button,
// Body
add_profile_btn: gtk::Button,
remove_profile_btn: gtk::Button,
profiles_textview: gtk::TextView,
// Creating profile
creating_profile: CreatingProfileUI,
// Statusbar
statusbar: gtk::Statusbar,
}
impl UI {
fn init(&self) {
self.add_profile_btn
.connect_clicked(move |_| { &self.creating_profile.window.run(); });
}
}
And I get this error:
error[E0477]: the type `[closure#src/main.rs:109:46: 111:6 self:&UI]` does not fulfill the required lifetime
--> src/main.rs:109:30
|
109 | self.add_profile_btn.connect_clicked(move |_| {
| ^^^^^^^^^^^^^^^
|
= note: type must satisfy the static lifetime
You can't move non-static references into GTK callbacks. You need something static or something heap allocated (e.g. in a Box/RefCell/Rc/etc.).
Callbacks are not called from the scope where you connect to the signal, but at some later point from the main loop. It is required that whatever you pass into the closure is still alive then, which would be anything 'static, heap-allocated or allocated on the stack between main and where the main loop runs. The last part can't currently be nicely expressed with Rust/GTK-rs.
See the example at the bottom in the gtk-rs docs for an example. It uses an Rc<RefCell<_>>.

How to avoid mutex borrowing problems when using it's guard

I want my method of struct to perform in a synchronized way. I wanted to do this by using Mutex (Playground):
use std::sync::Mutex;
use std::collections::BTreeMap;
pub struct A {
map: BTreeMap<String, String>,
mutex: Mutex<()>,
}
impl A {
pub fn new() -> A {
A {
map: BTreeMap::new(),
mutex: Mutex::new(()),
}
}
}
impl A {
fn synchronized_call(&mut self) {
let mutex_guard_res = self.mutex.try_lock();
if mutex_guard_res.is_err() {
return
}
let mut _mutex_guard = mutex_guard_res.unwrap(); // safe because of check above
let mut lambda = |text: String| {
let _ = self.map.insert("hello".to_owned(),
"d".to_owned());
};
lambda("dd".to_owned());
}
}
Error message:
error[E0500]: closure requires unique access to `self` but `self.mutex` is already borrowed
--> <anon>:23:26
|
18 | let mutex_guard_res = self.mutex.try_lock();
| ---------- borrow occurs here
...
23 | let mut lambda = |text: String| {
| ^^^^^^^^^^^^^^ closure construction occurs here
24 | if let Some(m) = self.map.get(&text) {
| ---- borrow occurs due to use of `self` in closure
...
31 | }
| - borrow ends here
As I understand when we borrow anything from the struct we are unable to use other struct's fields till our borrow is finished. But how can I do method synchronization then?
The closure needs a mutable reference to the self.map in order to insert something into it. But closure capturing works with whole bindings only. This means, that if you say self.map, the closure attempts to capture self, not self.map. And self can't be mutably borrowed/captured, because parts of self are already immutably borrowed.
We can solve this closure-capturing problem by introducing a new binding for the map alone such that the closure is able to capture it (Playground):
let mm = &mut self.map;
let mut lambda = |text: String| {
let _ = mm.insert("hello".to_owned(), text);
};
lambda("dd".to_owned());
However, there is something you overlooked: since synchronized_call() accepts &mut self, you don't need the mutex! Why? Mutable references are also called exclusive references, because the compiler can assure at compile time that there is only one such mutable reference at any given time.
Therefore you statically know, that there is at most one instance of synchronized_call() running on one specific object at any given time, if the function is not recursive (calls itself).
If you have mutable access to a mutex, you know that the mutex is unlocked. See the Mutex::get_mut() method for more explanation. Isn't that amazing?
Rust mutexes do not work the way you are trying to use them. In Rust, a mutex protects specific data relying on the borrow-checking mechanism used elsewhere in the language. As a consequence, declaring a field Mutex<()> doesn't make sense, because it is protecting read-write access to the () unit object that has no values to mutate.
As Lukas explained, your call_synchronized as declared doesn't need to do synchronization because its signature already requests an exclusive (mutable) reference to self, which prevents it from being invoked from multiple threads on the same object. In other words, you need to change the signature of call_synchronized because the current one does not match the functionality it is intended to provide.
call_synchronized needs to accept a shared reference to self, which will signal to Rust that it can be called from multiple threads in the first place. Inside call_synchronized a call to Mutex::lock will simultaneously lock the mutex and provide a mutable reference to the underlying data, carefully scoped so that the lock is held for the duration of the reference:
use std::sync::Mutex;
use std::collections::BTreeMap;
pub struct A {
synced_map: Mutex<BTreeMap<String, String>>,
}
impl A {
pub fn new() -> A {
A {
synced_map: Mutex::new(BTreeMap::new()),
}
}
}
impl A {
fn synchronized_call(&self) {
let mut map = self.synced_map.lock().unwrap();
// omitting the lambda for brevity, but it would also work
// (as long as it refers to map rather than self.map)
map.insert("hello".to_owned(), "d".to_owned());
}
}

Resources