Help passing data out of a closure that takes a reference

โš“ Rust    ๐Ÿ“… 2025-10-09    ๐Ÿ‘ค surdeus    ๐Ÿ‘๏ธ 5      

surdeus

Hi there! Relative rust newcomer here.

I'm trying to make use of a library (midir) that allows me to handle MIDI messages via a callback that I provide.

I can do something simple, like printing out the messages as they arrive, but to do something more complex (i.e. have a synth engine play those notes), I think I need to be able to pass the message outside of the callback. But I'm only given a reference to the data, and whenever I try to do that, I run into errors.

As an example, here's a modification of the library's test_read_input.rs example code illustrating what I was trying to do:

use std::error::Error;
use std::io::{stdin, stdout, Write};
use std::sync::mpsc;
use std::thread;

use midir::{Ignore, MidiInput};

fn main() {
    match run() {
        Ok(_) => (),
        Err(err) => println!("Error: {}", err),
    }
}

fn run() -> Result<(), Box<dyn Error>> {
    let mut input = String::new();

    let mut midi_in = MidiInput::new("midir reading input")?;
    midi_in.ignore(Ignore::None);

    // Get an input port (read from console if multiple are available)
    let in_ports = midi_in.ports();
    let in_port = match in_ports.len() {
        0 => return Err("no input port found".into()),
        1 => {
            println!(
                "Choosing the only available input port: {}",
                midi_in.port_name(&in_ports[0]).unwrap()
            );
            &in_ports[0]
        }
        _ => {
            println!("\nAvailable input ports:");
            for (i, p) in in_ports.iter().enumerate() {
                println!("{}: {}", i, midi_in.port_name(p).unwrap());
            }
            print!("Please select input port: ");
            stdout().flush()?;
            let mut input = String::new();
            stdin().read_line(&mut input)?;
            in_ports
                .get(input.trim().parse::<usize>()?)
                .ok_or("invalid input port selected")?
        }
    };

    println!("\nOpening connection");
    let in_port_name = midi_in.port_name(in_port)?;

    let (tx, rx) = mpsc::channel(); // create a message channel

    // _conn_in needs to be a named parameter, because it needs to be kept alive until the end of the scope
    let _conn_in = midi_in.connect(
        in_port,
        "midir-read-input",
        move |stamp, message, _| {
            println!("{}: {:?} (len = {})", stamp, message, message.len());
            tx.send((stamp, message)).unwrap(); // try to send the midi message down the channel, this fails
        },
        (),
    )?;

    thread::spawn(move || {
        for msg in rx {
            println!("received {msg:?}");
        }
    });

    println!(
        "Connection open, reading input from '{}' (press enter to exit) ...",
        in_port_name
    );

    input.clear();
    stdin().read_line(&mut input)?; // wait for next enter key press

    println!("Closing connection");
    Ok(())
}

and the error that this produces:

error[E0521]: borrowed data escapes outside of closure
  --> examples/test_read_input_message_passing.rs:58:13
   |
50 |     let (tx, rx) = mpsc::channel(); // create a message channel
   |          -- `tx` declared here, outside of the closure body
...
56 |         move |stamp, message, _| {
   |                      ------- `message` is a reference that is only valid in the closure body
57 |             println!("{}: {:?} (len = {})", stamp, message, message.len());
58 |             tx.send((stamp, message)).unwrap(); // try to send the midi message down the channel...
   |             ^^^^^^^^^^^^^^^^^^^^^^^^^ `message` escapes the closure body here
   |
   = note: requirement occurs because of the type `std::sync::mpsc::Sender<(u64, &[u8])>`, which makes the generic argument `(u64, &[u8])` invariant
   = note: the struct `std::sync::mpsc::Sender<T>` is invariant over the parameter `T`
   = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance

For more information about this error, try `rustc --explain E0521`.

Could someone show me where I'm going wrong? I think I understand the idea behind the error (trying to use a reference outside where it is valid), but not how to fix it. I imagine this might be a relatively obvious answer to a more experienced Rust programmer, and I just haven't fully understood some concept yet.

(Cross-posting this from How to implement message queues using the callbacks? ยท Boddlnagg/midir ยท Discussion #172 ยท GitHub because I think this might be me not understanding a general rust concept)

2 posts - 2 participants

Read full topic

๐Ÿท๏ธ Rust_feed