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.12k stars 283 forks source link

Running panic: DroppableDisposedException: Try to use `RustArc<dynamic>` ... #2260

Closed dbsxdbsx closed 2 weeks ago

dbsxdbsx commented 2 weeks ago

Env: Frb v2.3.0, win10, dart Dart 3.5.0, Rust 1.79.0 I got runnning panic

DroppableDisposedException (DroppableDisposedException: Try to use `RustArc<dynamic>` after it has been disposed)

within my real project, which can be reproduced with the following simple example code:

use flutter_rust_bridge::frb;

#[flutter_rust_bridge::frb(sync)] // Synchronous mode for simplicity of the demo
pub fn greet(name: String) -> String {
    format!("Hello, {name}!")
}

#[flutter_rust_bridge::frb(init)]
pub fn init_app() {
    // Default utilities - feel free to customize
    flutter_rust_bridge::setup_default_user_utils();
}

#[frb(opaque)]
pub enum MyEnum {
    Item1(Struct1),
    Item2(Struct2),
}
impl MyEnum {
    #[frb(sync)]
    pub fn new(i: usize) -> Self {
        match i {
            0 => Self::Item1(Struct1 {}),
            1 => Self::Item2(Struct2 {}),
            _ => panic!("Invalid index"),
        }
    }

    #[frb(sync)]
    pub fn get_name(&self) -> String {
        match self {
            MyEnum::Item1(_) => "Item1".to_string(),
            MyEnum::Item2(_) => "Item2".to_string(),
        }
    }
}

pub struct Struct1 {}

pub struct Struct2 {}

// #[frb(sync)] // same issue for the sync version
//  pub fn process_enum(vec: Vec<MyEnum>) {// same issue
pub fn process_enum(vec: &[MyEnum]) {
    // process the vector of MyEnum
}

with dart code:

import 'package:flutter/material.dart';
import 'package:test_frb/rust/api/simple.dart';
import 'package:test_frb/rust/frb_generated.dart';

class NameProvider {
  String getName() {
    return "Tom";
  }
}

Future<void> main() async {
  await RustLib.init();
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    final x = MyEnum(i: BigInt.from(1));
    test([x]);
    String name = x.getName(); // PANIC here

    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('flutter_rust_bridge quickstart')),
        body: Center(
          child: Text(
              'Action: Call Rust `greet("$name")`\nResult: `${greet(name: name)}`'),
        ),
      ),
    );
  }

  Future test(List<MyEnum> providers) async {
    await processEnum(vec: providers);
  }
}

At first, I think it is a pure dart issue related to the dart instance lifetime copy and reference issue, so I test this pure dart code:

import 'package:flutter/material.dart';
import 'package:test_frb/rust/api/simple.dart';
import 'package:test_frb/rust/frb_generated.dart';
import 'package:test_frb/rust/proxy/node.dart';

class NameProvider {
  String getName() {
    return "Tom";
  }
}

Future<void> main() async {
  await RustLib.init();
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    final x = NameProvider();
    String name = test([x]);
    String name2 = x.getName();

    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('flutter_rust_bridge quickstart')),
        body: Center(
          child: Text(
              'Action: Call Rust `greet("$name")`\nResult: `${greet(name: name)}`'),
        ),
      ),
    );
  }

  String test(List<NameProvider> providers) {
    return providers[0].getName();
  }
}

which occurs no panic.

I've also referred this 2 issues, but seems they are not related to the issue here. So I suspect maybe this maybe a new bug related to the lifetime handling acrros dart/rust within frb?

fzyzcjy commented 2 weeks ago

vec: Vec<MyEnum> will take ownership of the MyEnum. So it seems that this is expected that the x is not useable after calling the test function. Just like in pure rust:

pub fn process_enum(vec: &[MyEnum]) {}

let x = MyEnum {};
process_enum(&[x]);
x.getName(); // <-- COMPILE ERROR

Try &[RustAutoOpaque<MyEnum>] if this is supported by frb

fzyzcjy commented 2 weeks ago

For future readers: DO NOT DOWNLOAD THE LINK ABOVE!

The comment of @Evul88 seems to be downloading virus or something like that, e.g. look at https://github.com/valeoai/BEVContrast/issues/3 and https://github.com/search?q=commenter%3AEvul88&type=issues.

(If I get it wrong, feel free to ping me and I apologize!)

fzyzcjy commented 2 weeks ago

Same for @Satyam1Deepu

dbsxdbsx commented 2 weeks ago

&[RustAutoOpaque]

Thanks advice, it does can:

pub async fn process_enum(vec: &[RustAutoOpaque<MyEnum>]) {
    // process the vector of MyEnum
    // let item = (*vec.first().unwrap().read().await).clone(); // if clonnable
    let item = &(*(vec.first().unwrap().read().await));

    let mut vec_clone = Vec::with_capacity(vec.len());
    for item in vec {
        let item_ref = item.read().await;
        vec_clone.push(item_ref.clone());
    }
    // TODO: how to get vec_ref: &[MyEnum] from vec: &[RustAutoOpaque<MyEnum>]

}

But I didn't find way directly get ref &[MyEnum] from the input param. And the vec_clone version poisoned the inner type to be Clonnable. Otherwise, everything is fine.

Hopefully, in future, users can directly use &[MyEnum] with no lifetime issue. I guess maybe it need to wait until the proxy macro feature be fully used first.

fzyzcjy commented 2 weeks ago

Yes I think so - maybe we need to enhance the proxy to implement this, though that may be somehow nontrivial work

github-actions[bot] commented 3 days ago

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.