fzyzcjy / flutter_rust_bridge

Flutter/Dart <-> Rust binding generator, feature-rich, but seamless and simple.
https://fzyzcjy.github.io/flutter_rust_bridge/
MIT License
4.11k stars 281 forks source link

How to use generics traits? #2214

Open NightBlaze opened 1 month ago

NightBlaze commented 1 month ago

Not sure is it an issue or I do something wrong.

Demo project available here: https://github.com/NightBlaze/generics-frb

I use stateful architecture with the next structure for each screen:

Dart part:

  1. UI (screen.dart file https://github.com/NightBlaze/generics-frb/blob/main/lib/root_screen.dart ) • it's just UI without logic which calls Rust code and receive responses via stream.

Rust part:

  1. Logic (screen_name_logic.rs file https://github.com/NightBlaze/generics-frb/blob/main/rust/src/api/screens/root_screen/root_screen_logic.rs ) • it's exposes ScreenNameLogic struct without any public fields, just functions. • it implements interior mutability pattern • it holds view_model_sink: Option<StreamSink<ScreenNameViewModel>> field • it holds a state of a screen

  2. State (screen_name_state.rs file https://github.com/NightBlaze/generics-frb/blob/main/rust/src/api/screens/root_screen/root_screen_state.rs ) • it's a simple struct. • it has a pub(crate) fn render(&self) -> ScreenNameViewModel to create a view model. Technically it's Into trait but I decided to not use it.

  3. ViewModel (screen_name_view_model.rs file https://github.com/NightBlaze/generics-frb/blob/main/rust/src/api/screens/root_screen/root_screen_view_models.rs ) • it's a simple struct without any logic.

So every ScreenNameLogic has a function to render state like this one:

fn render_state(&self) {
    if let Some(sink) = self.view_model_sink.as_ref() {
        if let Err(error) = sink.add(self.state.render()) {
            debug_log(format!("[ScreenName] Fail to add view model to sink. Error: {:?}", error));
        }
        return;
    }
    debug_log("[ScreenName] Expected to be view_model_sink not null but it's null".to_string());
}

and it's a good candidate to make it generics ( https://github.com/NightBlaze/generics-frb/blob/main/rust/src/api/screens/renderer.rs )

pub(crate) trait Renderable {
    type ViewModel;

    fn render(&self) -> Self::ViewModel;
}

pub(crate) fn render<T, U>(
    sink: &Option<StreamSink<T>>,
    state: &U,
    screen_name: &str,
)
where
    T: SseEncode,
    U: Renderable<ViewModel = T>,
{
    if let Some(sink) = sink.as_ref() {
        if let Err(error) = sink.add(state.render()) {
            println!("[{}] Fail to add view model to sink. Error: {:?}", screen_name, error);
        }
        return;
    }
    println!("[{}] Expected to be sink not null but it's null", screen_name);
}

flutter_rust_bridge_codegen generate works without any errors but there is an error in frb_generated.rs

cannot find type `ViewModel` in this scope

I tried to make all pub but still have the error.

So my questions are:

  1. How to use generic traits?
  2. Will there be an error "duplication of ViewModel name" in the code when I'll implemented the Renderable trait for another struct State?

I use frb v. 2.1.0

fzyzcjy commented 1 month ago

IMHO generics is not supported yet - thus it may not be able to be used currently. However, is good to have it implemented! Feel free to PR, alternatively I may work on it later, but since this is a nontrivial new feature, I cannot guarantee it will be done very soon.

NightBlaze commented 1 month ago

Got it. Thanks.

I'll be glad to create a PR but don't have enough proficient in Rust and Dart :(

fzyzcjy commented 1 month ago

You are welcome and it's OK!