Playing around with GTK

So playing around with GTK for the first time, both as gtk-rs and GTK at all. I actually had another help post already typed up however I found the problem while writing it, apparently the Gtk calendar starts counting months from Jan = 0, crazy.

Anyway the other question I have was just regarding how to deal with more general mutable variables between GTK elements. As I said, complete GTK novice, complete GTK-rs novice and still a pretty big noob at Rust in general so please forgive the question if it seems silly.

Lets say I have a counter, or some kind of total, perhaps drawing on input from a SpinButton or something. I press a button, it adds the content to a variable or pushes to a vec, structure, something like that.

So first I try the obvious, use a &mut in the closure. Lifetime of the closure might outlive the variable. Fair enough, makes sense. Then I use the clone! macro commonly used in the GTK examples to pass it in, but ofc thats cloning it, not what I want. So clone! an Rc, but then immutable, so Rc Refcell it, clone! and borrow_mut inside the closure. That works.

So my question is: is there a nicer way of doing that?

Also I intend on compiling this for Windows later, currently Linux. Am I going to encounter problems other than GTK's somewhat out of place look on Windows, and for distributing it (with the MSVC toolchain preferably) what libs do I need to package up with it?

1 Like

In the case of a counter I'd use an atomic integer wrapped in Rc (e.g. Rc<AtomicUsize>). Otherwise you'll need to use some form of interior mutability so that multiple things can have access to your object at the same time and still be able to mutate it.

Your code might then turn into something like this (probs won't compile):

let counter = Rc::new(AtomicUsize::new(0));

let counter_handle = counter.clone();
some_button.connect_clicked(|_| { 
  // increment the counter and get the old value back
  let old_value = counter_handle.fetch_add(1, Ordering::SeqCst);

  // tell the SpinButton to update
});

Shouldn't that be either Arc<AtomicUsize> or Rc<Cell<usize>>, depending on whether thread safety is needed or not?

Shouldn’t that be either Arc<AtomicUsize> or Rc<Cell<usize>>

Most probably. GTK doesn't do multithreaded so a Rc<Cell<usize>> is probably the best option.

The only reason I reached for atomics is because incrementing a cell would require separate get/set steps, and a RefCell<T> can panic when you try to get a mutable reference to the inner thing. In comparison atomics are easier to use.

Note that using atomics in a single-threaded scenario can have huge performance drawbacks – especially when using Ordering::SeqCst. This can prevent compiler from doing optimizations, and will also emit expensive instructions, especially on (some) ARMs. If you insist on using atomics in a single-threaded scenario, Ordering::Relaxed is enough.

RefCell<T> indeed can panic, but the usual way way to avoid this is to put the RefCell as a field of a struct. That way, you have more control on when the RefCell is borrowed. But I agree – in this case RefCell for a simple integer is an overkill.

The Cell approach indeed requires get() + set() to increment. I agree it's not pleasant to use. But instead of reaching for atomics, one can implement a wrapper struct with a simple increment method. That will spare a future reader from asking themself Why this Ordering is used there? Which ordering should I choose? Which threads have access to this counter?.

struct Counter {
    cell: Cell<usize>
}
4 Likes