antoyo / relm

Idiomatic, GTK+-based, GUI library, inspired by Elm, written in Rust
MIT License
2.43k stars 78 forks source link

self not usable if using a macro in view! #189

Open antoyo opened 4 years ago

antoyo commented 4 years ago
view! {
    gtk::Label {
        text: &format!("{}", self.model.counter),
    },
}
sanpii commented 4 years ago

Is there a workaround for this issue?

antoyo commented 4 years ago

Maybe something like this:

view! {
    gtk::Label {
        text: {
            let counter = &self.model.counter;
            &format!("{}", counter)
        },
    },
}
antoyo commented 3 years ago

This seems non-trivial to fix and the above workaround doesn't work.

As macro arguments can be anything, even invalid Rust code, it makes it very hard to actually parse them to find usage of self.model.

Does anyone have any solution to this problem?

What macros do you use in this context? Maybe we could only support a limited set of macros, like format!.

sanpii commented 3 years ago

A minimalist example to reproduce this bug:

use gtk::GtkWindowExt;
use relm::Widget;

#[relm_derive::widget]
impl Widget for Win {
    fn model() -> String {
        "#189".to_string()
    }

    fn update(&mut self, _: ()) {
    }

    view! {
        gtk::Window {
            title: &format!("{}", self.model),
        }
    }
}

fn main() {
    Win::run(()).expect("Win::run failed");
}
cargo check ``` $ cg check Checking rust v0.1.0 (/home/sanpi/test/rust) error[E0424]: expected value, found module `self` --> src/main.rs:15:35 | 5 | impl Widget for Win { | --- this function doesn't have a `self` parameter ... 15 | title: &format!("{}", self.model), | ^^^^ `self` value is a keyword only available in methods with a `self` parameter | help: add a `self` receiver parameter to make the associated `fn` a method | 5 | impl Widget for &self, Win { | ^^^^^^ error: aborting due to previous error For more information about this error, try `rustc --explain E0424`. error: could not compile `rust` To learn more, run the command again with --verbose. ```
cargo explain ```rust #![feature(prelude_import)] #[prelude_import] use std::prelude::v1::*; #[macro_use] extern crate std; use gtk::GtkWindowExt; use relm::Widget; #[allow(dead_code, missing_docs)] pub struct Win { gtkwindow1: gtk::Window, model: String, } pub struct __WinWidgets { pub gtkwindow1: gtk::Window, } impl Widget for Win { #[allow(unused_variables)] fn view(relm: &::relm::Relm, __relm_model: Self::Model) -> Self { let gtkwindow1: gtk::Window = unsafe { if !gtk::is_initialized_main_thread() { if gtk::is_initialized() { { ::std::rt::begin_panic("GTK may only be used from the main thread.") }; } else { { ::std::rt::begin_panic( "GTK has not been initialized. Call `gtk::init` first.", ) }; } } use relm::StaticType; use relm::{Cast, FromGlibPtrNone}; let values: &[::relm::Value] = &[]; let mut parameters = []; ::gtk::Widget::from_glib_none(::relm::g_object_newv( ::relm::ToGlib::to_glib(>k::Window::static_type()), 0u32, parameters.as_mut_ptr(), ) as *mut _) .downcast() .unwrap() }; gtkwindow1.set_title(&{ let res = ::alloc::fmt::format(::core::fmt::Arguments::new_v1( &[""], &match (&self.model,) { (arg0,) => [::core::fmt::ArgumentV1::new( arg0, ::core::fmt::Display::fmt, )], }, )); res }); ::gtk::WidgetExt::show(>kwindow1); Win { gtkwindow1: gtkwindow1, model: __relm_model, } } type Root = gtk::Window; fn root(&self) -> Self::Root { self.gtkwindow1.clone() } } impl ::relm::Update for Win { type Msg = (); type Model = String; type ModelParam = (); fn update(&mut self, _: ()) {} fn model(_: &::relm::Relm, _: ()) -> String { "#189".to_string() } } impl ::relm::WidgetTest for Win { type Widgets = __WinWidgets; fn get_widgets(&self) -> __WinWidgets { __WinWidgets { gtkwindow1: self.gtkwindow1.clone(), } } } impl Win {} fn main() { Win::run(()).expect("Win::run failed"); } ```

Using __relm_model works fine.

Some ideas:

  1. Rename __relm_model to model;
  2. Allow only self.model and replace it by __relm_model;
  3. Add an argument to view!:
    view!(model) {
    gtk::Window {
        title: &format!("{}", model),
    }
    }

    I like the second one, but it maybe not the easier…

antoyo commented 3 years ago

If we were to choose the second option, how would that be implemented? By iterating over all the tokens in the macro and replacing the appropriate sequence of tokens (self, ., models) by __relm_model everywhere?