“Tokio channel rust” 可以翻译为 “Tokio通道Rust”。

huangapple go评论72阅读模式
英文:

Tokio channel rust

问题

Here's the translated code:

我正在尝试使用Tokio实现一个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);
}

Regarding the error you mentioned, it's related to moving the tx variable into the closure. To fix it, I changed channel::&lt;i32&gt;() to channel::<i32>() to explicitly specify the type of the channel, which allows the tx variable to be moved into the closure correctly.

英文:

I am trying to implement a Promise system with Tokio, I simulate the callback and the resolve, this is the code:

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() -&gt; i32 {
    let (tx, rx) = channel::&lt;i32&gt;();
    task_prom(move |x| tx.send(x).unwrap()).await;
    rx.await.unwrap()
}

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

When I try to run, I get this error:

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


--&gt; src\main.rs:17:24
    |
16  |     let (tx, rx) = channel::&lt;i32&gt;();
    |          -- 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&lt;i32&gt;`, which does not implement the `Copy` trait
    |               captured by this `Fn` closure
    |
note: `tokio::sync::oneshot::Sender::&lt;T&gt;::send` takes ownership of the receiver `self`, which moves `tx`
   --&gt; 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) -&gt; Result&lt;(), T&gt; {
    |

                 ^^^^

I see the Tokio's documentation, but I don't know why I have this error. Thanks.

答案1

得分: 4

以下是翻译好的部分:

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

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

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

error[E0382]: 使用已移动的值:`callback`
 --&gt; src/main.rs:9:13
  |
6 | async fn task_prom(callback: impl FnOnce(i32)) {
  |                    -------- 移动发生是因为 `callback` 具有类型 `impl FnOnce(i32)`,它不实现 `Copy` 特性
...
9 |             callback(x);
  |             ^^^^^^^^---
  |             |
  |             `callback` 由于此调用而移动,之前的循环迭代中
  |
note: 此值实现了 `FnOnce`,这会在调用时导致它被移动
 --&gt; src/main.rs:9:13
  |
9 |             callback(x);
  |             ^^^^^^^^
help: 考虑进一步限制此约束
  |
6 | async fn task_prom(callback: impl FnOnce(i32) + Copy) {
  |                                               ++++++

有关此错误的更多信息,请尝试 `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 对分支何时被执行相当聪明。

英文:

Since a oneshot channel can only be used once, you can't capture and then use it in a Fn or FnMut closure. The closure must consume it, so it needs to be a FnOnce closure.

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

Unfortunately, this still doesn't compile.

error[E0382]: use of moved value: `callback`
 --&gt; 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
 --&gt; 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 does not statically know how many times a for loop is run. It also doesn't statically know which branch in an if expression will run. So you need to restructure your code to ensure callback is only run once. How you do this will depend on the function, but in this case you can just move it outside the loop.

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;
}

You could also insert a return in the same path as the callback call.

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;
    }
}

Aside from the iterator in for loops and the condition in if, match, and while, rust is pretty smart about when branches can be taken.

答案2

得分: 1

I do not fully understand what you are trying to do here, but your code can be made to compile by changing impl Fn(i32) to impl FnOnce(i32), and then calling callback once after the for loop instead of within the loop:

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;
}

As the error message mentions, send moves out of tx, so the closure can only be executed once, so you need to use FnOnce, the trait that indicates a function can only be called once. Then you need to move callback out of the loop body so that Rust knows that it will only be called once.

All of this is because you are using a oneshot channel that can only be sent on once; this is fine so long as you only need to execute the callback once. Other channels would allow you to call send more than once if that's what you want.

Edit: fixed code to match @drewtato's more detailed answer since that is more correct (there should be a final sleep after the callback is executed, which I had missed at first).

英文:

I do not fully understand what you are trying to do here, but your code can be made to compile by changing impl Fn(i32) to impl FnOnce(i32), and then calling callback once after the for loop instead of within the loop:

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;
}

As the error message mentions, send moves out of tx, so the closure can only be executed once, so you need to use FnOnce, the trait that indicates a function can only be called once. Then you need to move callback out of the loop body so that Rust knows that it will only be called once.

All of this is because you are using a oneshot channel that can only be sent on once; this is fine so long as you only need to execute the callback once. Other channels would allow you to call send more than once, if that's what you want.

Edit: fixed code to match @drewtato's more detailed answer since that is more correct (there should be a final sleep after the callback is executed, which I had missed at first).

huangapple
  • 本文由 发表于 2023年5月15日 09:56:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/76250438.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定