fzyzcjy / flutter_rust_bridge

Flutter/Dart <-> Rust binding generator, feature-rich, but seamless and simple.

Home Page:https://fzyzcjy.github.io/flutter_rust_bridge/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Crate compilation error when using mirrored third party Enum in StreamSink

SilverMira opened this issue · comments

Describe the bug

Encountered a rust compilation error when I had both a function to return a third-party Enum to Dart land and a function that writes said Enum to a StreamSink.

cargo check errors
error[E0277]: the trait bound `allo_isolate::ffi::DartCObject: From<MyEnum>` is not satisfied
   --> src\frb_generated.rs:100:47
    |
100 | ...                   .stream_sink::<_, crate::api::simple::MyEnum>(),
    |                        -----------      ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `From<MyEnum>` is not implemented for `allo_isolate::ffi::DartCObject`, which is required by `MyEnum: IntoDart`
    |                        |
    |                        required by a bound introduced by this call
    |
    = help: the following other types implement trait `From<T>`:
              <allo_isolate::ffi::DartCObject as From<DartOpaque>>
              <allo_isolate::ffi::DartCObject as From<RustOpaqueBase<T, A>>>
    = note: required for `MyEnum` to implement `Into<allo_isolate::ffi::DartCObject>`
    = note: required for `MyEnum` to implement `IntoDart`
note: required by a bound in `flutter_rust_bridge::rust2dart::context::TaskRust2DartContext::<Rust2DartCodec>::stream_sink`
   --> C:\Users\Mira\.cargo\registry\src\index.crates.io-6f17d22bba15001f\flutter_rust_bridge-2.0.0-dev.28\src\rust2dart\context.rs:33:12
    |
30  |     pub fn stream_sink<T, D>(&self) -> StreamSinkBase<T, Rust2DartCodec>
    |            ----------- required by a bound in this associated function
...
33  |         D: IntoDart,
    |            ^^^^^^^^ required by this bound in `TaskRust2DartContext::<Rust2DartCodec>::stream_sink`

error[E0277]: the trait bound `MyEnum: IntoIntoDart<MyEnum>` is not satisfied
   --> src\frb_generated.rs:100:44
    |
100 | ...                   .stream_sink::<_, crate::api::simple::MyEnum>(),
    |                        -----------   ^ the trait `IntoIntoDart<MyEnum>` is not implemented for `MyEnum`
    |                        |
    |                        required by a bound introduced by this call
    |
    = help: the trait `IntoIntoDart<mirror_MyEnum>` is implemented for `MyEnum`
    = help: for that trait implementation, expected `mirror_MyEnum`, found `MyEnum`
note: required by a bound in `flutter_rust_bridge::rust2dart::context::TaskRust2DartContext::<Rust2DartCodec>::stream_sink`
   --> C:\Users\Mira\.cargo\registry\src\index.crates.io-6f17d22bba15001f\flutter_rust_bridge-2.0.0-dev.28\src\rust2dart\context.rs:32:12
    |
30  |     pub fn stream_sink<T, D>(&self) -> StreamSinkBase<T, Rust2DartCodec>
    |            ----------- required by a bound in this associated function
31  |     where
32  |         T: IntoIntoDart<D>,
    |            ^^^^^^^^^^^^^^^ required by this bound in `TaskRust2DartContext::<Rust2DartCodec>::stream_sink`

For more information about this error, try `rustc --explain E0277`.
error: could not compile `rust_lib_repro_frb` (lib) due to 2 previous errors

Also, while preparing this repro, I noticed that apparently the mirrored 3rd party type must also implement Clone due to a derive(Clone) in frb_generated.rs, is this intended?

#[derive(Clone)]
pub struct mirror_MyEnum(crate::api::simple::MyEnum);

Steps to reproduce

  1. flutter_rust_bridge_codegen create repro_frb
  2. cd repro_frb
  3. cargo new --lib rust-3rdparty
  4. Modify and add Enum in rust-3rdparty/src/lib.rs
    #[derive(Clone)]
    pub enum MyEnum {
        Foo,
        Bar,
        Baz,
    }
  5. Modify rust/Cargo.toml to have dependency on rust-3rdparty
    [dependencies]
    rust-3rdparty = { path = "../rust-3rdparty" }
  6. Modify rust/src/api/simple.rs
    pub use rust_3rdparty::MyEnum;
    
    use crate::frb_generated::StreamSink;
    
    #[flutter_rust_bridge::frb(mirror(MyEnum))]
    pub enum _MyEnum {
        Foo,
        Bar,
        Baz,
    }
    
    pub fn get_enum() -> MyEnum {
        MyEnum::Foo
    }
    
    pub fn stream_enum(_sink: StreamSink<MyEnum>) {}
  7. flutter_rust_bridge_codegen generate

Logs

N/A

Expected behavior

Rust bridge can compile

Generated binding code

No response

OS

Windows

Version of flutter_rust_bridge_codegen

2.0.0-dev.28

Flutter info

No response

Version of clang++

No response

Additional context

No response

Hmm, do you mean it is OK if we do either be return value or be in stream sink, but error if we do both; Or can the minimal reproducible sample be further reduced to halves?

I noticed that apparently the mirrored 3rd party type must also implement Clone due to a derive(Clone) in frb_generated.rs, is this intended?

If your enum does not support clone (or have other special properties), maybe just make it opaque instead of mirroring it.

The error happens as long as there is a mirrored Enum used in a StreamSink, even if the mirrored Enum is never used as a return value. Cargo check shows that the compilation error is within the generated code when instantiating the actual StreamSink during the .stream_sink call

StreamSink::new(
    context
        .rust2dart_context()
        .stream_sink::<_, MyEnum>(),
)

If MyEnum is not mirrored, then FRB generates it as opaque, which does compile, but makes it harder to use on the Dart land

While messing around with the generated code, it looks like the crate can compile if I manually changed the following.

StreamSink::new(
    context
        .rust2dart_context()
        .stream_sink::<_, mirror_MyEnum>(), // codegen had MyEnum as the generic type originally
)

Additionally, not sure if I encountered the same issue but in another way, I'm getting the same compilation error if there is a StreamSink<Option<TestStruct>> where TestStruct is opaque.

#[flutter_rust_bridge::frb(opaque)]
pub struct TestStruct {}
pub fn stream_struct(_sink: StreamSink<TestStruct>) {}
pub fn stream_struct_option(_sink: StreamSink<Option<TestStruct>>) {}

I looked at the StreamSink instantiation for stream_struct and stream_struct_option and noticed there was a difference. The ordinary StreamSink<TestStruct> which is compiling fine uses Local_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedrust_asyncRwLockTestStruct as the generic type while the problematic StreamSink<Option<TestStruct>> was just using Option<TestStruct> as the generic type. Manually replacing the Option<TestStruct> generic type within frb_generated.rs with Option<Local_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedrust_asyncRwLockTestStruct> in fact fixed the compilation issue and Dart land is even receiving the objects correctly.

frb_generated.rs
fn wire_stream_struct_impl(
    port_: flutter_rust_bridge::for_generated::MessagePort,
    ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr,
    rust_vec_len_: i32,
    data_len_: i32,
) {
    FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::<flutter_rust_bridge::for_generated::SseCodec,_,_>(flutter_rust_bridge::for_generated::TaskInfo{ debug_name: "stream_struct", port: Some(port_), mode: flutter_rust_bridge::for_generated::FfiCallMode::Stream }, move || { 
            let message = unsafe { flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire(ptr_, rust_vec_len_, data_len_) };
            let mut deserializer = flutter_rust_bridge::for_generated::SseDeserializer::new(message);
            deserializer.end(); move |context|  {
                    transform_result_sse((move ||  {
                         Result::<_,()>::Ok(crate::api::simple::stream_struct(StreamSink::new(context.rust2dart_context().stream_sink::<_,Local_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedrust_asyncRwLockTestStruct>())))
                    })())
                } })
}
fn wire_stream_struct_option_impl(
    port_: flutter_rust_bridge::for_generated::MessagePort,
    ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr,
    rust_vec_len_: i32,
    data_len_: i32,
) {
    FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::<flutter_rust_bridge::for_generated::SseCodec, _, _>(
        flutter_rust_bridge::for_generated::TaskInfo {
            debug_name: "stream_struct_option",
            port: Some(port_),
            mode: flutter_rust_bridge::for_generated::FfiCallMode::Stream,
        },
        move || {
            let message = unsafe {
                flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire(
                    ptr_,
                    rust_vec_len_,
                    data_len_,
                )
            };
            let mut deserializer =
                flutter_rust_bridge::for_generated::SseDeserializer::new(message);
            deserializer.end();
            move |context| {
                transform_result_sse((move || {
                    Result::<_, ()>::Ok(crate::api::simple::stream_struct_option(StreamSink::new(
                        context
                            .rust2dart_context()
                            .stream_sink::<_, Option<TestStruct>>(), // <---- Problematic?
                    )))
                })())
            }
        },
    )
}

it looks like the crate can compile if I manually changed the following

Briefly check it and it looks like it should be mirror_Something. For example, from pure_dart example we can see some working code looks like:

image

Feel free to PR to fix it! Alternatively I will work on it in the next batch (hopefully within several days)

Manually replacing the Option generic type within frb_generated.rs with

Good observation! Then looks like it is missing generating this.

This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new issue.