有没有可能让这个 Rust 代码更容易推理?

Is it possible to make this Rust code easier to reason about?

我正在阅读这段 Rust 代码,但我几乎没有理解所有互斥锁和句柄的心智能力。让 Rust 之神高兴都是开销,而且很难专注于实际发生的事情。看看:

#[tauri::command]
fn spawn(param: String, window: Window<Wry>) {
    let window_arc = Arc::new(Mutex::new(window));

    // Spawn bin
    let (mut rx, child) = tauri::api::process::Command::new_sidecar("bin")
        .expect("failed to create binary command")
        .args([param])
        .spawn()
        .expect("Failed to spawn sidecar");
    let child_arc = Arc::new(Mutex::new(child));

    // Handle data from bin
    let window = window_arc.clone();
    let (handle, mut handle_rx) = broadcast::channel(1);
    let handle_arc = Arc::new(Mutex::new(handle));
    tauri::async_runtime::spawn(async move {
        loop {
            tokio::select! {
                _ = handle_rx.recv() => {
                    return;
                }
                Some(event) = rx.recv() => {
                    if let CommandEvent::Stdout(line) = &event {
                        let data = decode_and_xor(line.clone());
                        println!("Data from bin: {}", data);
                        window.lock().unwrap().emit("from_bin", data).expect("failed to emit message");
                    }
                    if let CommandEvent::Stderr(line) = &event {
                        println!("Fatal error bin: {}", &line);
                        window.lock().unwrap().emit("bin_fatal_error", line).expect("failed to emit message");
                    }
                }
            }
            ;
        }
    });
    let window = window_arc.clone();
    let window_cc = window.clone();
    window_cc.lock().unwrap().listen("kill_bin", move |event| {
        let handle = handle_arc.clone();
        handle.lock().unwrap().send(true).unwrap();
        window.lock().unwrap().unlisten(event.id());
    });

    // Handle data to bin
    let window = window_arc.clone();
    tauri::async_runtime::spawn(async move {
        let child_clone = child_arc.clone();
        let (handle, handle_rx) = broadcast::channel(1);
        let handle_rx_arc = Arc::new(Mutex::new(handle_rx));
        let handle_arc = Arc::new(Mutex::new(handle));
        let window_c = window.clone();
        window.lock().unwrap().listen("to_bin", move |event| {
            let handle_rx = handle_rx_arc.clone();
            if handle_rx.lock().unwrap().try_recv().is_ok() {
                window_c.lock().unwrap().unlisten(event.id());
                return;
            }
            // Send data to bin
            let payload = String::from(event.payload().unwrap());
            let encrypted = xor_and_encode(payload) + "\n";
            println!("Data to send: {}", event.payload().unwrap());
            child_clone.clone().lock().unwrap().write(encrypted.as_bytes()).expect("could not write to child");
        });
        let window_c = window.clone();
        window.lock().unwrap().listen("kill_bin", move |event| {
            let handle = handle_arc.clone();
            handle.lock().unwrap().send(true).unwrap();
            window_c.lock().unwrap().unlisten(event.id());
        });
    });
}

所有这些 Arcs、Mutexes 和克隆都是必需的吗?我将如何以 Rust 惯用的方式清理它,使它更容易看到发生了什么?

Are all these Arcs, Mutexes and clones necessary?

可能不是,您似乎在过度克隆 -- 并重新包装并发结构,但您必须查看特定的 API

例如假设 broascast::channel 是 Tokio 的,它是为并发使用而设计的(这有点关键),因此发送方被设计为可克隆(对于多个生产者),您可以根据需要从发送方创建尽可能多的接收方。

没有必要包裹在 Arc 中,特别是没有必要用锁来保护它们,它们被设计成按原样工作。

此外,在这种情况下,它更没有必要,因为您只有一个发送者任务和一个接收者任务,共享。使用它们时,您也不需要 克隆 它们。所以例如

    let handle_arc = Arc::new(Mutex::new(handle));
    [...]
    window_cc.lock().unwrap().listen("kill_bin", move |event| {
        let handle = handle_arc.clone();
        handle.lock().unwrap().send(true).unwrap();
        window.lock().unwrap().unlisten(event.id());
    });

我很确定可以

    window_cc.lock().unwrap().listen("kill_bin", move |event| {
        handle.send(true).unwrap();
        window.lock().unwrap().unlisten(event.id());
    });

这将 move 闭包内的 handle,然后发送。 Sender 是内部可变的,所以它不需要锁定来发送事件(这会破坏重点)。