Rust中如何在foreach操作变量后继续使用该变量?
解决Actix Web中ClientRequest闭包移动后无法复用的问题
问题代码
use actix_web::{HttpRequest, HttpResponse, Responder}; use awc::ClientRequest; pub async fn proxy(req: HttpRequest) -> impl Responder { let response = construct_request(req).send().await; let body = response.unwrap().body().await.expect(""); HttpResponse::Ok().body(body) } fn construct_request(req: HttpRequest) -> ClientRequest { let client = awc::Client::default(); let mut new_req = client.get("url"); req.headers().iter().for_each(|headerPair| { new_req.append_header((headerPair.0, headerPair.1)); }); new_req.content_type("application/json") }
编译器错误信息
error[E0507]: cannot move out of `new_req`, a captured variable in an `FnMut` closure --> src/routes/proxy.rs:18:9 | 15 | let mut new_req = client.get("http://localhost:8001/ping"); | ----------- captured outer variable 16 | 17 | req.headers().iter().for_each(|headerPair| { | ___________________________________- 18 | | new_req.append_header((headerPair.0, headerPair.1)); | | ^^^^^^^ ------------------------------------------- `new_req` moved due to this method call | | | | | move occurs because `new_req` has type `ClientRequest`, which does not implement the `Copy` trait 19 | | }); | |_____- captured by this `FnMut` closure | note: this function takes ownership of the receiver `self`, which moves `new_req` --> /Users/{sanitized}/awc-3.0.1/src/request.rs:186:30 | 186 | pub fn append_header(mut self, header: impl TryIntoHeaderPair) -> Self { | ^^^^ warning: variable does not need to be mutable --> src/routes/proxy.rs:15:9 | 15 | let mut new_req = client.get("http://localhost:8001/ping"); | ----^^^^^^^ | | | help: remove this `mut` | = note: `#[warn(unused_mut)]` on by default error[E0382]: use of moved value: `new_req` --> src/routes/proxy.rs:21:5 | 15 | let mut new_req = client.get("http://localhost:8001/ping"); | ----------- move occurs because `new_req` has type `ClientRequest`, which does not implement the `Copy` trait 16 | 17 | req.headers().iter().for_each(|headerPair| { | ------------ value moved into closure here 18 | new_req.append_header((headerPair.0, headerPair.1)); | ------- variable moved due to use in closure ... 21 | new_req.append_header(("custom", req.headers().get("custom").unwrap())) | ^^^^^^^ value used here after move
问题原因
awc::ClientRequest的append_header方法采用获取所有权的设计(接收self而非&mut self),调用后会返回一个新的ClientRequest实例,原实例的所有权会被转移。在for_each闭包中,第一次调用new_req.append_header就会把new_req的所有权移走,后续迭代和循环结束后都无法再使用原变量。
解决方案
方法1:使用fold链式累积
fold方法可以在迭代过程中累积一个值,正好适配这种需要每次用新实例替换旧实例的场景:
fn construct_request(req: HttpRequest) -> ClientRequest { let client = awc::Client::default(); req.headers() .iter() .fold(client.get("url"), |current_req, header_pair| { current_req.append_header((header_pair.0, header_pair.1)) }) .content_type("application/json") }
这里以初始请求实例为起点,每次迭代将当前请求传入闭包,调用append_header后返回新实例作为下一次迭代的输入,最后直接链式调用content_type完成配置。
方法2:手动for循环赋值
如果更习惯显式循环结构,可以用普通for循环,每次将append_header返回的新实例重新赋值给变量:
fn construct_request(req: HttpRequest) -> ClientRequest { let client = awc::Client::default(); let mut new_req = client.get("url"); for header_pair in req.headers().iter() { new_req = new_req.append_header((header_pair.0, header_pair.1)); } new_req.content_type("application/json") }
每次循环都更新new_req为最新的实例,避免所有权移动导致的问题,循环结束后可以正常继续使用变量。
补充说明
两种方案都能解决编译器错误,其中fold写法更简洁,而手动循环可读性更强。原代码中的unused_mut警告会自动消失:fold场景不需要可变变量,手动循环场景中new_req确实需要可变属性。
内容的提问来源于stack exchange,提问作者dantdj
相关产品推荐
相关产品推荐

