时雄海峡 rust 病

r6hnlfcb  于 2023-05-17  发布在  其他
关注(0)|答案(2)|浏览(108)

我试图用时雄实现一个Promise系统,我模拟了回调和解析,这是代码:

use std::time::Duration;

use tokio::sync::oneshot::{channel};
use tokio::time::sleep;

async fn task_prom(callback: impl Fn(i32)) {
    for x in 1..=10 {
        if x == 10 {
            callback(x);
        }
        sleep(Duration::from_secs(1)).await;
    }
}

async fn handler() -> i32 {
    let (tx, rx) = channel::<i32>();
    task_prom(move |x| tx.send(x).unwrap()).await;
    rx.await.unwrap()
}

#[tokio::main]
async fn main() {
    let res = handler().await;
    println!("{}", res);
}

当我尝试运行时,我得到这个错误:

error[E0507]: cannot move out of `tx`, a captured variable in an `Fn` closure

--> src\main.rs:17:24
    |
16  |     let (tx, rx) = channel::<i32>();
    |          -- captured outer variable
17  |     task_prom(move |x| tx.send(x).unwrap()).await;
    |               -------- ^^ ------- `tx` moved due to this method call
    |               |        |
    |               |        move occurs because `tx` has type `tokio::sync::oneshot::Sender<i32>`, which does not implement the `Copy` trait
    |               captured by this `Fn` closure
    |
note: `tokio::sync::oneshot::Sender::<T>::send` takes ownership of the receiver `self`, which moves `tx`
   --> C:\Users\titof\.cargo\registry\src\index.crates.io-6f17d22bba15001f\tokio-1.28.1\src\sync\oneshot.rs:594:21
    |
594 |     pub fn send(mut self, t: T) -> Result<(), T> {
    |

                 ^^^^

我看到了时雄的文档,但我不知道为什么我有这个错误。谢谢。

bf1o4zei

bf1o4zei1#

由于oneshot通道只能使用一次,因此不能捕获它,然后在FnFnMut闭包中使用它。闭包必须使用它,所以它需要是一个FnOnce闭包。

async fn task_prom(callback: impl FnOnce(i32))

不幸的是,这仍然无法编译。

error[E0382]: use of moved value: `callback`
 --> src/main.rs:9:13
  |
6 | async fn task_prom(callback: impl FnOnce(i32)) {
  |                    -------- move occurs because `callback` has type `impl FnOnce(i32)`, which does not implement the `Copy` trait
...
9 |             callback(x);
  |             ^^^^^^^^---
  |             |
  |             `callback` moved due to this call, in previous iteration of loop
  |
note: this value implements `FnOnce`, which causes it to be moved when called
 --> src/main.rs:9:13
  |
9 |             callback(x);
  |             ^^^^^^^^
help: consider further restricting this bound
  |
6 | async fn task_prom(callback: impl FnOnce(i32) + Copy) {
  |                                               ++++++

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

Rust并不静态地知道for循环运行了多少次。它也不静态地知道if表达式中的哪个分支将运行。因此,您需要重新构造代码,以确保callback只运行一次。如何实现这一点取决于函数,但在这种情况下,您可以将其移动到循环之外。

async fn task_prom(callback: impl FnOnce(i32)) {
    for x in 1..10 {
        sleep(Duration::from_secs(1)).await;
    }
    callback(10);
    sleep(Duration::from_secs(1)).await;
}

您还可以在callback调用的相同路径中插入return

async fn task_prom(callback: impl FnOnce(i32)) {
    for x in 1..=10 {
        if x == 10 {
            callback(x);
            sleep(Duration::from_secs(1)).await;
            return;
        }
        sleep(Duration::from_secs(1)).await;
    }
}

除了for循环中的迭代器和if、match和while中的条件,rust在何时可以进行分支方面非常聪明。

xzlaal3s

xzlaal3s2#

我不完全理解你在这里想做什么,但是你的代码可以通过将impl Fn(i32)改为impl FnOnce(i32)来编译,然后在for循环之后调用callback一次,而不是在循环中:

async fn task_prom(callback: impl FnOnce(i32)) {
    for _ in 1..10 {
        sleep(Duration::from_secs(1)).await;
    }
    callback(10);
    sleep(Duration::from_secs(1)).await;
}

正如错误消息中提到的,send移出了tx,所以闭包只能执行一次,所以你需要使用FnOnce,这个trait表示一个函数只能被调用一次。然后你需要将callback移出循环体,这样Rust就知道它只会被调用一次。
所有这一切都是因为您使用的是一个只能发送一次的oneshot通道;只要您只需要执行一次回调,这就很好。如果您需要,其他通道将允许您多次调用send
编辑:修正了代码,以匹配@drewtato更详细的答案,因为这是更正确的(在回调执行后应该有一个最后的睡眠,我一开始错过了)。

相关问题