These are chat archives for rust-lang/rust

1st
Dec 2018
David O'Connor
@David-OConnor
Dec 01 2018 03:05

Looking for a quick guess about whether this is an easy lifetime fix, or something fundamentally-flawed: Using wasm-bindgen to try to create a state-based virtual DOM. If I try to do anything related to my app's state in an event closure, I receive lifetime errors:

 let cb = move |ws_event: web_sys::Event| {
                &ws_event.prevent_default();  // works
                web_sys::console::log_1(&ws_event.into());  // works
//              self.model;  // Causes the error below; What I really need to do is update the model here, but just invoking its name raises the error.
            };

          let closure = Closure::wrap(Box::new(cb) as Box<FnMut(_)>); 

          let el_et: web_sys::EventTarget = el_ws.clone().into(); 
         el_et.add_event_listener_with_callback(event.as_str(), closure.as_ref().unchecked_ref()).unwrap();
        closure.forget();

Error:

error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
   --> src\vdom.rs:149:22
    |
149 |               let cb = move |ws_event: web_sys::Event| {
    |  ______________________^
150 | |                 &ws_event.prevent_default();
151 | |                 web_sys::console::log_1(&ws_event.into());
15... 156 | |             };
    | |_____________^
    |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 129:5...
   --> src\vdom.rs:129:5
    |
129 | /     fn el_vdom_to_websys(&mut self, el_vdom: &El<Ms>) -> web_sys::Element {
130 | |         let el_ws = self.document.create_element(el_vdom.tag.as_str()).unwrap();
131 | |         for (name, val) in &el_vdom.attrs.vals {
132 | |             el_ws.set_attribute(name, val).unwrap();
...   |
161 | |         el_ws
162 | |     }
    | |_____^
    = note: ...so that the types are compatible:
            expected &mut vdom::App<Ms, Mdl>
               found &mut vdom::App<Ms, Mdl>
    = note: but, the lifetime must be valid for the static lifetime...
note: ...so that the type `[closure@src\vdom.rs:149:22: 156:14 self:&mut vdom::App<Ms, Mdl>]` will meet its required lifetime bounds
   --> src\vdom.rs:158:13

I have a weak understanding of lifetimes etc, but I'm suspicious the error means my strategy is broken. There are a few github projects that solve this issue (ruukh, draco, squark, an official wasm-bindgen MVC etc), but they're nontrivial to understand. I'm suspicious the solution involves a cocktail of smart pointers.

laurent bernabé
@loloof64
Dec 01 2018 08:37
Hi everyone, I am facing a somewhat similar issue. I need a way to share the same reference that can be mutably borrowed by several closure in the same function :
fn my_func(&mut self) {
    let object = self.object_field_as_struct;

    receiver1.act_as_closure(move || {
         object_as_smart_ptr1.func_borrowing_mutably();
    });

    receiver2.act_as_closure(move || {
         object_as_smart_ptr2.func_borrowing_mutably();
    });

    // and so on
}
So what is the best Smart Pointer I could use to do so ?
laurent bernabé
@loloof64
Dec 01 2018 08:42
(Receivers are just local variables)
Code is mandatory simplified here, because my snippet is too complex in order to be posted here
laurent bernabé
@loloof64
Dec 01 2018 10:14


fn open_select_promotion_dialog(&mut self, start_cell: (u8, u8), end_cell: (u8, u8)) {
        // elided code

        let queen_button = Button::new();
        queen_button.add(&queen_image);
        queen_button.connect_clicked({
            let logic_ref = self.logic.borrow_mut();
            let drawing_area_ref = self.drawing_area.clone();
            let dialog_ref = dialog_ref.clone();
            let start_cell = start_cell.clone();
            let end_cell = end_cell.clone();

            move |_button| {
                logic_ref.do_move::<Chess>(start_cell, end_cell, Some(Role::Queen));
                drawing_area_ref.queue_draw();

                dialog_ref.close();
            }
        });

        // elided code
How can solve the following error that this snippet rises ?
error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
   --> src/chess_position_trainer/graphic/chessboard.rs:232:40
    |
232 |             let logic_ref = self.logic.borrow_mut();
    |                                        ^^^^^^^^^^
    |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 218:5...
   --> src/chess_position_trainer/graphic/chessboard.rs:218:5
    |
218 | /     fn open_select_promotion_dialog(&mut self, start_cell: (u8, u8), end_cell: (u8, u8)) {
219 | |         let dialog = Dialog::new();
220 | |         dialog.set_modal(true);
221 | |
...   |
262 | |         dialog_ref.show_all();
263 | |     }
    | |_____^
note: ...so that reference does not outlive borrowed content
   --> src/chess_position_trainer/graphic/chessboard.rs:232:29
    |
232 |             let logic_ref = self.logic.borrow_mut();
    |                             ^^^^^^^^^^
    = note: but, the lifetime must be valid for the static lifetime...
note: ...so that the type `[closure@src/chess_position_trainer/graphic/chessboard.rs:238:13: 243:14 logic_ref:std::cell::RefMut<'_, chess_position_trainer::logic::chessgame::ChessGame>, start_cell:(u8, u8), end_cell:(u8, u8), drawing_area_ref:std::rc::Rc<gtk::DrawingArea>,dialog_ref:std::rc::Rc<gtk::Dialog>]` will meet its required lifetime bounds
   --> src/chess_position_trainer/graphic/chessboard.rs:231:22
    |
231 |         queen_button.connect_clicked({
    |                      ^^^^^^^^^^^^^^^
Here the ChessBoard struct
#[derive(Clone)]
pub struct ChessBoard
{
    drawing_area: Rc<DrawingArea>,
    reversed: bool,
    logic: RefCell<ChessGame>,
    cells_size: u32,
    moved_piece: Option<MovedPiece>,
    piece_images: HashMap<char, Pixbuf>,
}

#[derive(Clone, Debug)]
struct MovedPiece
{
    piece_type: Piece,
    coords_x: f64,
    coords_y: f64,
    start_file: u8,
    start_rank: u8
}
Denis Lisov
@tanriol
Dec 01 2018 11:41
@loloof64 Once again your closure needs to own an Rc that you will access the ChessGame through...
laurent bernabé
@loloof64
Dec 01 2018 11:43
Thanks, I'm trying to rethink my Smart pointers gestion, once I landed in a code without any error, but it panicked because I tried to borrow the RefCell as mutable several times at once.
The faulty method is OpenPromotionSelector
Denis Lisov
@tanriol
Dec 01 2018 12:04
Could you test that version with RUST_BACKTRACE=1?
I don't know the specifics of GTK callbacks, so don't understand where the reentrance happens...
laurent bernabé
@loloof64
Dec 01 2018 12:05
Yes
thread 'main' panicked at 'already mutably borrowed: BorrowError', libcore/result.rs:1009:5
stack backtrace:
   0: std::sys::unix::backtrace::tracing::imp::unwind_backtrace
             at libstd/sys/unix/backtrace/tracing/gcc_s.rs:49
   1: std::sys_common::backtrace::print
             at libstd/sys_common/backtrace.rs:71
             at libstd/sys_common/backtrace.rs:59
   2: std::panicking::default_hook::{{closure}}
             at libstd/panicking.rs:211
   3: std::panicking::default_hook
             at libstd/panicking.rs:227
   4: std::panicking::rust_panic_with_hook
             at libstd/panicking.rs:477
   5: std::panicking::continue_panic_fmt
             at libstd/panicking.rs:391
   6: rust_begin_unwind
             at libstd/panicking.rs:326
   7: core::panicking::panic_fmt
             at libcore/panicking.rs:77
   8: core::result::unwrap_failed
             at libcore/macros.rs:26
   9: <core::result::Result<T, E>>::expect
             at libcore/result.rs:835
  10: <core::cell::RefCell<T>>::borrow
             at libcore/cell.rs:804
  11: chess_position_trainer::chess_position_trainer::graphic::chessboard::ChessBoard::get_chessboard::{{closure}}
             at src/chess_position_trainer/graphic/chessboard.rs:140
  12: core::ops::function::impls::<impl core::ops::function::Fn<A> for &'a F>::call
             at libcore/ops/function.rs:247
  13: gtk::auto::widget::draw_trampoline
             at /home/laurent_bernabe/.cargo/registry/src/github.com-1ecc6299db9ec823/gtk-0.5.0/src/auto/widget.rs:3470
  14: <unknown>
  15: <unknown>
  16: g_signal_emit_valist
  17: g_signal_emit
  18: <unknown>
  19: gtk_container_propagate_draw
  20: <unknown>
  21: <unknown>
  22: <unknown>
  23: <unknown>
  24: <unknown>
  25: <unknown>
  26: gtk_container_propagate_draw
  27: <unknown>
  28: <unknown>
  29: <unknown>
  30: <unknown>
  31: gtk_main_do_event
  32: <unknown>
  33: <unknown>
  34: <unknown>
  35: <unknown>
  36: g_closure_invoke
  37: <unknown>
  38: g_signal_emit_valist
  39: g_signal_emit
  40: <unknown>
  41: <unknown>
  42: <unknown>
  43: g_main_context_dispatch
  44: <unknown>
  45: g_main_loop_run
  46: gtk_dialog_run
  47: <O as gtk::auto::dialog::DialogExt>::run
             at /home/laurent_bernabe/.cargo/registry/src/github.com-1ecc6299db9ec823/gtk-0.5.0/src/auto/dialog.rs:159
  48: chess_position_trainer::chess_position_trainer::graphic::chessboard::ChessBoard::open_promotion_selector
             at src/chess_position_trainer/graphic/chessboard.rs:266
  49: chess_position_trainer::chess_position_trainer::graphic::chessboard::ChessBoard::handle_mouse_released
             at src/chess_position_trainer/graphic/chessboard.rs:204
  50: chess_position_trainer::chess_position_trainer::graphic::chessboard::ChessBoard::get_chessboard::{{closure}}
             at src/chess_position_trainer/graphic/chessboard.rs:150
  51: core::ops::function::impls::<impl core::ops::function::Fn<A> for &'a F>::call
             at libcore/ops/function.rs:247
  52: gtk::auto::widget::event_trampoline
             at /home/laurent_bernabe/.cargo/registry/src/github.com-1ecc6299db9ec823/gtk-0.5.0/src/auto/widget.rs:3482
  53: <unknown>
  54: g_signal_emit_valist
  55: g_signal_emit
  56: <unknown>
  57: <unknown>
  58: gtk_main_do_event
  59: <unknown>
  60: <unknown>
  61: g_main_context_dispatch
  62: <unknown>
  63: g_main_loop_run
  64: gtk_main
  65: gtk::auto::functions::main
             at /home/laurent_bernabe/.cargo/registry/src/github.com-1ecc6299db9ec823/gtk-0.5.0/src/auto/functions.rs:273
  66: chess_position_trainer::main
             at src/main.rs:21
  67: std::rt::lang_start::{{closure}}
             at libstd/rt.rs:74
  68: std::panicking::try::do_call
             at libstd/rt.rs:59
             at libstd/panicking.rs:310
  69: __rust_maybe_catch_panic
             at libpanic_unwind/lib.rs:103
  70: std::rt::lang_start_internal
             at libstd/panicking.rs:289
             at libstd/panic.rs:392
             at libstd/rt.rs:58
  71: std::rt::lang_start
             at libstd/rt.rs:74
  72: main
  73: __libc_start_main
  74: _start
I have an idea of the cause, but don't know how to solve it
laurent bernabé
@loloof64
Dec 01 2018 12:10
In the chessboard struct, the method handle_mouse_release , which handle the end of DragAndDrop on my custom drawing
line 204 : let selected_role = self.open_promotion_selector();
I'm trying to give to open_promotion_selector() a &mut self; which here is not possible at runtime
because of line 198 : if let Some(ref moved_piece) = self.moved_piece {
and handle_mouse_release def on line 188 : fn handle_mouse_released(&mut self, coords: (f64, f64)){
and line 150 EventType::ButtonRelease => chess_board_ref_3.borrow_mut().handle_mouse_released(coords),
Then going back to line 144: let chess_board_ref_3 = chess_board_ref.clone();
line 136 : let chess_board_ref = Rc::new(RefCell::new(chess_board));
laurent bernabé
@loloof64
Dec 01 2018 12:15
(That's should be enough to understand the issue here)
Denis Lisov
@tanriol
Dec 01 2018 12:15
Ok, so here's the problem...
Dialog::run waits for the dialog to be closed. However, while the user is interacting with the dialog, GTK may need to redraw your widget.
laurent bernabé
@loloof64
Dec 01 2018 12:19
Ok
Thanks, so I need a way to force the dialog to close before redrawing the Widget ?
Denis Lisov
@tanriol
Dec 01 2018 12:21
When that happens, your closure tries to borrow the ChessBoard to paint everything... however, it's already borrowed mutably to handle the mouse event.
That's not a good idea IMO... the user will not be ok with that :-)
laurent bernabé
@loloof64
Dec 01 2018 12:21
Thanks, I understand better. So I must postpone the refresh
Denis Lisov
@tanriol
Dec 01 2018 12:24
There are multiple options :-)
(a) display the choice inline in your widget without popping out a modal dialog
(b) drop the borrow before running the dialog and re-borrow afterwards
laurent bernabé
@loloof64
Dec 01 2018 12:27
Yes, (b) solution is what I'd like to aim to :smile:
How can a borrow be dropped ?
Denis Lisov
@tanriol
Dec 01 2018 12:29
Then you'll need to pass a reference to the RefCell as an argument, not something borrowed from it. Yes, this will not look nice :-(
laurent bernabé
@loloof64
Dec 01 2018 12:30
Thanks. Maybe not nice, but at least the user is forced to make a decision (letting queen if ever he can find a way to close the modal)
and not to rely on default value that he may forget to change
Denis Lisov
@tanriol
Dec 01 2018 12:32
I'd probably try incorporating this logic into the state machine of the app...
laurent bernabé
@loloof64
Dec 01 2018 12:32
How ? What would you store in the Struct ?
Ah. I understand better suddenly : you would store the piece selection inside the struct, and change to whatever is chosen in the dialog.
Indeed, very better. I'll try this evening when coming back. Thanks again :smile:
Denis Lisov
@tanriol
Dec 01 2018 12:38
I'd add a pending_promotion: Option<PromotionDetails> field... if it's not None, the main event handlers are inactive, additional hints for the player are painted and the promotion UI is shown :-)
laurent bernabé
@loloof64
Dec 01 2018 16:40
Thank you :smile: I'm gonna to try this way :smile:
Andrew Champion
@aschampion
Dec 01 2018 17:47

How do I effectively use cfg for feature flags in a where clause? I have blanket implementations like

impl<D: SomeTrait> SomeOtherTrait for Bar<D>
    where Backend<Postgres, D>: SomeOtherTrait,
                  Backend<Mongo, D>: SomeOtherTrait,

etc. Now I'm putting each of these backend providers (Postgres, etc.) behind feature flags, but there doesn't seem to be a way to get this into a where clause, and creating the appropriate clause manually based upon the combination of features is 2^N in the number of backend providers. Obviously putting a marker trait on each provider (e.g., impl<D: SomeTrait, P: BackendProvider> .... where Backend<P, D>) doesn't work either because it's not constrained by the impl and the type system doesn't do universals.

I'm guessing proc macros are the only way out here, just getting exhausted of having so many.
Denis Lisov
@tanriol
Dec 01 2018 19:01
This is a weird situation... do you mean that enabling an additional backend (enabling a feature flag) may cause the impl to stop being applicable?