Very simple RefCell call in gtk program fails to compile, needs IsA trait(?)

I'm writing a simple graphing application using gtk. There is a state structure Board that has information needed in the callback functions. My understanding is that since GDK uses just the single event loop Rc is not needed, but RefCell can be used by itself. When I first saw the error I thought it might have to do with a gtk object in the struct, but even after removing all members from the struct it gets the same error, that it is missing a trait for IsA.

I think this means it needs to be able to map the structure to C code and then return it to Rust for the callback. To fulfill the IsA trait it needs the ToGlibPtr trait, and for that all it needs is a to_glib_none() implementation. I've looked for a sample method or a description of what it is supposed to do but have not been able to find one. Is this right? Originally I wrote the code without RefCell, cloning Board in the callback closure, and that worked (except the different handlers had different states because they had their own cloned instances of Board), so that data can be passed.

Could someone help by pointing me towards doc or example of how to do this, or if there is a better way point me towards that? It almost makes me long for global variables :frowning:

Thanks

$ cargo build
   Compiling mytest v0.1.0 (/Users/russell/personal/projects/tetrii/rust/mytest)
error[E0599]: the method `get` exists for struct `RefCell<Board>`, but its trait bounds were not satisfied
  --> src/main.rs:17:21
   |
17 |             p_board.get().board.keyboard_input(key);
   |                     ^^^ method cannot be called on `RefCell<Board>` due to unsatisfied trait bounds
   |
   = note: the following trait bounds were not satisfied:
           `RefCell<Board>: IsA<TreeModel>`
           which is required by `RefCell<Board>: gtk4::prelude::TreeModelExtManual`
           `RefCell<Board>: IsA<gtk4::gio::Settings>`
           which is required by `RefCell<Board>: gtk4::prelude::SettingsExtManual`
           `RefCell<Board>: IsA<UnixFDList>`
           which is required by `RefCell<Board>: gtk4::prelude::UnixFDListExtManual`
           `RefCell<Board>: IsA<DBusInterface>`
           which is required by `RefCell<Board>: gtk4::prelude::DBusInterfaceExt`
#![allow(unused)]
use gtk::prelude::*;
use std::cell::RefCell;

#[derive(Debug, Clone)]
pub struct Board {
}

impl Board {
    pub fn new() -> Board {
        let mut board = Board{
        };
        let p_board = RefCell::new(board);

        let key_handler = gtk::EventControllerKey::new();
        key_handler.connect_key_pressed(move |_ctlr, key, _code, _state| {
            p_board.get().keyboard_input(key);
            gtk::Inhibit(false)
        });
        board
    }
    
    fn keyboard_input(&self, key: gdk4::Key) {
    }
}

fn main() {
    let board = Board::new();
}

The error message is mainly telling you that a RefCell does not have a method called “.get()”. The fact that it lists a few not actually implemented traits and pretends “yeah the method kind-of exists and also not really” is mostly a red herring.

The main methods to access a RefCell are called .borrow() and .borrow_mut(); in this case, for a &self method like keyboard_input, .borrow() should be sufficient.

3 Likes

:flushed: Oh my goodness, how embarrassing. Hopefully the embarrassment and wasted day researching glib will remind me to check the obvious before leaping into the sea. Thank you for pointing it out.

1 Like

I think there’s three aspects to this experience, one that can be learned and one that the compiler can maybe improve upon, and one that’s simply bad luck / based on the unidiomatic patterns that come with a C++ binding.


As a first point, in my view the compiler error

the method `get` exists for struct `RefCell<Board>`, but its trait bounds were not satisfied

is not clear enough in that the missing trait bounds will essentially mean that the method does not exist due to the unsatisfied trat bounds. I.e., as mentioned above, this error is sort-of a red herring and I can understand the confusion it can cause. I believe there’s probably room for improvement in the wording of the error message.


As a second point, it it generally a very useful principle not to take a compilation error of the kind “the following trait bound is not satisfied” or similar statements about a type not implementing a trait, as any kind of actual hint that you are supposed to change this fact. Very often, unsatisfied trait bounds are unsatisfied for a reason. Of course, there are also cases where you do want to write a missing trait implementation, but it should always be a strongly considered option that a missing trait implementation is meant to be missing. (A common example I encounter in the forums every so often is compilation errors about missing Copy implementations on types that also can absolutely not implement Copy anyways; but the error message still makes some people think they are supposed to implement Copy, rather than changing the code that required the Copy bound).


A third point: The API design of the gtk crate. As a wrapper for C++ APIs, it’s most definitely full of unidiomatic and possibly confusing constructs. For example the IsA trait is somewhat re-modeling a class hierarchy on the Rust side. And a super large prelude with extension traits that have methods with names as generic as get isn’t the most typical thing either.

2 Likes

And something else I'm learning, after some more time trying to fool the compiler: Rc is needed, getting my initialization to finish and keeping a reference around doesn't seem possible (unless I'm again missing something). Adding Rc lets it work, so even if it is possible to avoid it there seems to be no reason to. I need to import Rust memory management into my C brain.

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.