Rust Closure Variable Capture

Rust Closure Variable Capture

(Jin Qing’s Column, Jan., 2025)

In tonic\examples\src\mock\mock.rs:

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    ...
    let mut client = Some(client);
    let channel = Endpoint::try_from("http://[::]:50051")?
        .connect_with_connector(service_fn(move |_: Uri| {
            let client = client.take();

            async move {
                if let Some(client) = client {
                    Ok(TokioIo::new(client))
                } else {
                ...
            }
        }))
        .await?;
    ...

Changing serivce_fn from:

        service_fn(move |_: Uri| {
            let client = client.take();

            async move {
                if let Some(client) = client {
                    Ok(TokioIo::new(client))
                } else {
                ...
            }
        })

moving client.take() into async block:

        service_fn(move |_: Uri| async move {
            let client = client.take();

            if let Some(client) = client {
                Ok(TokioIo::new(client))
            } else {
            ...
        })

produces build error:

error[E0525]: expected a closure that implements the `FnMut` trait, but this closure only implements `FnOnce`        
  --> examples\src\mock\mock.rs:35:44
   |
35 |         .connect_with_connector(service_fn(move |_: Uri| async move {
   |          ----------------------            ^^^^^^^^^^^^^ this closure implements `FnOnce`, not `FnMut`
   |          |
   |          the requirement to implement `FnMut` derives from here
36 |             let client = client.take();
   |                          ------ closure is `FnOnce` because it moves the variable `client` out of its environment
   |
   = note: required for `tower::util::ServiceFn<{closure@examples\src\mock\mock.rs:35:44: 35:57}>` to implement `tower::Service<tonic::transport::Uri>`

Rewrite to make the error more clear:

        service_fn(move |_: Uri| {
            async move {
                let client = client.take();

                if let Some(client) = client {
                    Ok(TokioIo::new(client))
                } else {
                ...
            }
        })

There are 2 captures in the closure: variable client is captured by the closure and then is captured again by the inner async block.

In the FnMut, we should not move the captured variable. But in this case, client is moved into the inner async block.

We must clone the variable client, or take the inner value of the client, to be moved into the inner async block.

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值