Library in rust. Where should I hold channel for async calls?

I am developing library in rust that would be used in iOS app. Library runs tokio event loop in one thread and iOS app will call functions written in rust from different threads.

Basically it works but rarely crashes.

FFI breaks safety. All FFI calls, callbacks are unsafe by default
https://doc.rust-lang.org/nomicon/ffi.html#asynchronous-callbacks - that article tells I must use channel or mutex.

So I use channel but I lack docs/example/knowledge how to make that right.

Could you advice some example or project that does rust library in correct canonical way and uses channels like advised in article?

e.g. I am not sure std::sync::mpsc::SyncSender could be put in static variable and be used by random thread calling rust via FFI.
Also I am not sure if futures::task::Task or task.notify() could be shared same way.

Why not ask compiler for help?
Just write Rust unit test that run one more thread (that emulate swift code) and cooperate with
main one that run tokio event loop.
If you can compile it without unsafe then you safe.

If your channels should communicate with a Tokio event loop, you want to use these channels.

Are you sure you can't pass the sender of the channel to the callback through the FFI?

Is sаfe FFI possible? I thought it is not possible in general. Objective C code has no lifetime limitations of rust code, etc. My rust code sends to and from byte arrays like *const u8. These all forces to use unsafe code blocks.

I found recently another interesting FFI tutorial but really interesting things for me are not yet written and marked as TODO. It is very common situation and I can't find good example for few weeks.
https://michael-f-bryan.github.io/rust-ffi-guide/async.html

If you know some idiomatic, canonical rust example or github project I would be happy to learn it because I spent weeks googling and there is only basic introductory examples.

Thanks

Rust unit test should use Rust API.
I mean let's say you wrap such Rust code:

struct Calculator {}
impl Calculator { 
   fn new(...) -> Self { ... } 
   fn calc(&mut self) { ... } 
}

And you wrote several extern "C" functions to "box" it, "unbox" and drop and call "Calculator::calc",
and you going to use it in background thread created by Swift.
Then you should write

#[test]
fn test_multithread() {
   let b = Box::new(Calculator::new());
   std::thread::spawn(move || {
       b.calc(); 
   });
}

And Rust compiler would check is it save to "send" "Calculator" to other thread and so on thing.

I use several times Rust libraries from Java and C++. In both cases Java and C++ were used for GUI.

And in this cases I use canonical way to report Rust async task success/error: GUI framework.

Both Android SDK/Java and Qt/C++ provides way to send message to main event loop,
so it was simple chain Rust -> callback -> C++ or Java code to send message to main event loop.

I am sure Swift and Apple SDK also have ability to send message to main GUI loop.

As example for C++ you can look at tests in my project,
it convert FnOnce on Rust side to std::future on C++ side,
so for example you can write on Rust spawn(future.and_then(FnOnce)),
and when will be called FnOnce then it triggered C++ std::future will inform C++ code that task is done. In the similar way I use Qt/QFuture to report event to Qt's main event loop when Rust future have been done.