Reputation: 673
I am using a Rust Midi library to receive and handle real-time Midi messages. It exposes a connect
function that accepts a callback which will be called for every Midi message that arrives. My plan was to forward these Midi messages to a channel. This is the minimal version of the code that still reproduces my issue (Rust Playground link):
use std::sync::mpsc;
fn main() {
let (tx, rx) = mpsc::sync_channel(0);
// The callback forwards all data it gets to the channel
connect(|data| tx.send(data).unwrap());
// `rx` will be given to some other part of the program here
}
// This is basically the function signature of my Midi library's `connect` function
// I *don't have control over it*, as it's part of that external library
fn connect<F>(callback: F)
where F: FnMut(&[u8]) + Send + 'static {}
After researching this issue for a while, I thought the solution was to add the move
keyword to the callback. This made sense to me, as the callback may live longer than the main function so tx
may have been dropped when the callback still needs it. move
forces the callback to capture its environment by value which should lead to tx
living exactly as long as the callback.
Yet move
changes nothing at all; the error message stays the same.
I noticed that when I change the parameter of the callback from &[u8]
to just u8
, move
actually does the trick. I have no idea why this could be.
Another question I've found explains how to send mutable slices over channels, but I have immutable slices, so I assume there is an easier solution than the one that has been explained there.
To close this off: I know that it's possible to restructure the code to avoid channels. However, I'm still interested in the solution so I can solve future issues with channels and callbacks on my own.
Upvotes: 1
Views: 108
Reputation: 673
It clicked inside my brain.
Additionally, to the move
keyword, the following is required:
connect
provides a reference to an array which lives for the scope of the callback. That means that when the callback finishes, the &[u8]
is inaccessible. I tried sending the reference outside of the callback which made no sense because then it would have to live longer.
The solution is to create an owned object from the slice, like a Vec
. That's trivially done by adding .to_vec()
:
connect(move |data| tx.send(data.to_vec()).unwrap());
The Vec
can be passed around freely without any lifetime conflictions. :)
Upvotes: 1