Iced 0.14.0-dev subsription type error

I’m trying to receive a message in the app via a subscription, by adding a receiver to it.

fn worker(ms: &MadStuff) -> impl Stream<Item = Msg> {
    stream::channel(100, async |mut output| {
        loop {
            let rx = &ms.rx;
            let rx_m: &mut Receiver<SData> = unsafe { std::mem::transmute(rx) };
            let input = rx_m.select_next_some().await;

            // Finally, we can optionally produce a message to tell the
            // `Application` the VNA Sdata is ready
            output.send(Msg::SdataRdy(input)).await;
        }
    })
}

// In your application's `subscription` function
fn subscription(&self) -> Subscription<Msg> {
    let rx = self.rx;
    let rx0 = unsafe { std::sync::Arc::<MadStuff>::into_inner(rx).unwrap_unchecked() };
    Subscription::run_with(rx0, Self::worker)
}

But i get error:

error[E0308]: mismatched types
→ src/iced_fe.rs:447:37
|
447 | Subscription::run_with(rx0, Self::worker)
| ---------------------- ^^^^^^^^^^^^ one type is more general than the other
| |
| arguments to this function are incorrect
|
= note: expected fn pointer for<'a> fn(&'a MadStuff) -> _
found fn item for<'a> fn(&'a MadStuff) -> impl iced::futures::Stream<Item = Msg> {MetricaFe::worker}
note: associated function defined here
→ /home/egan/.cargo/git/checkouts/iced-3e930cb38a75fe30/c99f4d0/futures/src/subscription.rs:219:12
|
219 | pub fn run_with<D, S>(data: D, builder: fn(&D) → S) → Self

It’s clear the framework wants to transmute fn(&D), but I can’t figure out what it’s need to be transmuted into. What should I do to finally get the code to compile?

So, I managed to make it so that the subscription could receive messages from the world of a healthy person.

In order to benefit from the subscription I had to:

  1. Make it marvelous BootFn, that take struct with refcell, that replaced with None after first call
/// It's workaround of it's Fn contract
fn boot(&self) -> MetricaFe {
    let inner = self.inner.replace(None);
    if let Some(data) = inner {
        let e_data: SData = SData::default();
...
  1. Make arg, that can passed to subscription worker:
// Used to make iced happy, when work with subscription
struct MadStuff {
    rx: *mut Receiver<Msg>,
    for_hash: u64,
}

impl MadStuff {
    /// Create Mad item
    /// `rx`        - receiver to wrap
    fn new(rx: &Receiver<Msg>) -> Self {
        unsafe {
            let rx0: *mut Receiver<Msg> = std::mem::transmute(rx);
            Self {
                rx: rx0,
                for_hash: 0xa24a2354,
            }
        }
    }
}

impl Hash for MadStuff {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.for_hash.hash(state);
    }
}
  1. Make subscription
    fn subscription(&self) -> Subscription<Msg> {
        let rx = self.rx.clone();
        let mad = MadStuff::new(&rx);

        Subscription::run_with(mad, |a| {
            let rx_m: &mut Receiver<Msg> = unsafe { std::mem::transmute(a.rx) };

            stream::channel(1, async |mut output| {
                loop {
                    let input = rx_m.select_next_some().await;
                    // Finally, we can optionally produce a message to tell the

                    // `Application` the VNA Sdata is ready
                    //output.send(Msg::SdataRdy(input)).await;
                    let _ = output.send(input).await;
                }
            })
        })
    }

Since Iced has an asynchronous exchange channel, to organize a bridge to the world of a healthy person, an additional synchronous stream is required that receives synchronous messages and converts them into asynchronous ones, as iced wants.

/// Proceed all actions, that need to be done
/// to make bridge between sync planar and async Iced
fn run(&mut self) {
    println!("Vac portal running");
    loop {
        let res = self.vac.rx.recv();
        println!("vec vac");
        match res {
            Err(RecvError) => {
                // Disconnected
                println!("VP: Disconnected from Vac");
                return;
            }
            Ok(PlanarMsg::ResERdy) => {
                let msg = Msg::EpsRx;
                let _res = executor::block_on(self.tx.send(msg));
            }

Your clanker needs some retraining.

We don’t transmute references here.

We don’t transmute references here.

Subsription take &self reference, so no change to get &mut Receiver<…> from &self without some bad magic. Anyway, with this subscription contact there is unlikely to be anything very good inside this code.

There are no examples in the documentation on how to solve this, and for now there is a strong desire not to edit the iced code.

The alternative of creating a channel in a subscription and sending it via an update message, where it’s unclear what to do with it, looks even worse.

A subscription should subscribe to messages (logically). To do this, it needs a mutable receiver. But it can’t receive a mutable receiver because its contract doesn’t allow it to receive mutable data. So there will be workarounds – everyone has their own way.

Same with run_with - it’s take & ref, not &mut - so there is no chance for simple and understandable code to appear here.

What exactly are you trying to do within worker? It feels like you could do this with a Task::stream or Task::sip instead

Receive messages from receiver and send to update. Messages to this receiver will be send from other modules and iced must handle this messages in its update.

In iced subscription description

“A request to listen to external events.

Besides performing async actions on demand with Task, most applications also need to listen to external events passively.”

And I want from the subscription exactly what is written in its description - listen external events.

I shared my experience – how I managed to add a receiver to the subscription. It currently works exactly as I understood the description – it listens to external events and forwards them to the update. I’m not sure if it could work any other way – from my perspective, I was forced to do it this way with function contracts. I have no choice but to use a bunch of hacks to utilize the functionality stated in the subscription description.

Have you had a look at the websocket example?

Yes. There no config passed to run and module, that shouldn’t know about iced, was written with aware of iced.

The code that was written in websocket example is either must be used in iced or can’t be used at all.

In my opinion, an unfortunate example. Would anyone in their right mind drag and drop gui pieces into a network subsystem? Web code should only have tx and rx to iced gui,

“listen to external events passively” also means “self-contained”, which it wouldn’t be if you pass something into it.

If you do want it to be able to “talk to your subscription”, what you do is create the subscription, then from inside the subscription you send a sender to your app via a message, which you can then use to send messages to the subscription (e.g. a new configuration every time it changes), for bidirectional communication.

the websocket example connects to an echo server which could be anything running on your network, so maybe spend a little longer trying to understand it

I don’t like it, and I find it extremely inconvenient. But by God’s grace, I managed to get it transmitted and working. Even if it came at the cost of a few hacks.

Trust me, it can’t be worse than the god awful transmute UB crap your clanker came up with.

1 Like

There is nothing worse than creating obstacles out of nowhere to achieve results.

your clanker

I don’t know what are you talking about. I read the documentation and did as it says.

who wrote this comment?

I don’t remember. I struggled with the framework for quite a while and, in general, achieved the result on my own. It’s an old project. Before I could get a decent subscription, I had to use a timer to poll.

Project link:

And yes, I’d be very surprised if there’s a working way to generate a solution for properly transmitting a receiver to a subscription (which is basically what I need). Because to do that, there needs to be an example of how to do it. I transmitted it, and now it works. If Jesus loves us, this solution will help someone achieve results.

Hopefully it’ll help someone realize what not to do.

You subscription sends back messages to your app that you receive in update. One of these should have an mpsc::Sender, which you then store in app state and use to send data to the subscription.

1 Like

Listening to your words, it seems to me that I probably needed to come to terms with the fact that I had to clone and edit Iced for my needs.