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.29k stars 301 forks source link

Unhandled Exception when returning an Err in Stream function #2415

Closed Tienisto closed 1 day ago

Tienisto commented 1 day ago

Describe the bug

The following Rust function throws an Unhandled Exception in Dart/Flutter:

pub async fn create_stream(stream_sink: StreamSink<i32>) -> Result<(), String> {
    tokio::time::sleep(Duration::from_secs(1)).await;

    let _ = stream_sink.add(42);

    tokio::time::sleep(Duration::from_secs(1)).await;

    Err("Some Error".to_string())
}

Throws:

[ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: Some Error
#0      SimpleDecoder.decode (package:flutter_rust_bridge/src/codec/base.dart:32:9)
#1      SseCodec._decode (package:flutter_rust_bridge/src/codec/sse.dart:45:55)
#2      SseCodec.decodeObject (package:flutter_rust_bridge/src/codec/sse.dart:35:12)
#3      Future._propagateToListeners.handleValueCallback (dart:async/future_impl.dart:861:45)
#4      Future._propagateToListeners (dart:async/future_impl.dart:890:13)
#5      Future._completeWithValue (dart:async/future_impl.dart:666:5)
#6      Future._asyncCompleteWithValue.<anonymous closure> (dart:async/future_impl.dart:736:7)
#7      _microtaskLoop (dart:async/schedule_microtask.dart:40:21)
#8      _startMicrotaskLoop (dart:async/schedule_microtask.dart:49:5)

This is difficult to catch on Dart side:

createStream().listen((event) {
  print('>>> Stream event: $event');
}, onError: (error) {
  // this is not called!
  print('>>> Stream error: $error');
});

Reproduction Repo: https://github.com/Tienisto/frb_stream_error

Steps to reproduce

Return Err in Rust that takes a StreamSink

Logs

Code generator works fine.

Expected behavior

No response

Generated binding code

No response

OS

No response

Version of flutter_rust_bridge_codegen

No response

Flutter info

No response

Version of clang++

No response

Additional context

No response

fzyzcjy commented 1 day ago

Hmm what about

pub async fn create_stream(stream_sink: StreamSink<i32>) {
    tokio::time::sleep(Duration::from_secs(1)).await;

    let _ = stream_sink.add(42);

    tokio::time::sleep(Duration::from_secs(1)).await;

    stream_sink.add_error("Some Error".to_string())
}
Tienisto commented 1 day ago

Yeah, this fixed the problem! Maybe we should add a warning that the return value of a function with StreamSink should be removed / has no usage

fzyzcjy commented 22 hours ago

That looks reasonable, and feel free to PR for this! The simplest form may be just add a few lines to the doc.