I'm searching a way to pass a variable url_path to this thread, as in this code:
async fn download_dist(all_paths: Vec<String>, mode: &str) {
let mut download_path = "";
let mut root_path = "";
let mut url_path = String::new();
if mode.contains("pool") {
root_path = POOL_ROOT_PATH;
url_path = format!("http://{}/{}", DEBIAN_REPOSITORY, DEBIAN_PATH);
download_path = DEBIAN_POOL_PATH;
} else {
root_path = DIST_ROOT_PATH;
url_path = format!("http://{}/{}/", DEBIAN_REPOSITORY, DEBIAN_DIST_PATH);
download_path = DEBIAN_DIST_PATH;
}
let responses = futures::stream::iter(all_paths.into_iter().map(move |path| {
tokio::spawn(async move {
println!("{}", url_path);
})
}))
.buffer_unordered(10)
.collect::<Vec<_>>();
responses.await;
}
Error is :
cannot move out of url_path
, a captured variable in an FnMut
closure
move out of url_path
occurs here
CodePudding user response:
.map
calls the closure you provide many times.
The problem is that your url_path
variable can only be moved to a thread once. So in order to move it to many threads (as you do), you have to clone
it:
async fn download_dist(all_paths: Vec<String>, mode: &str) {
let mut download_path = "";
let mut root_path = "";
let mut url_path = String::new();
if mode.contains("pool") {
root_path = POOL_ROOT_PATH;
url_path = format!("http://{}/{}", DEBIAN_REPOSITORY, DEBIAN_PATH);
download_path = DEBIAN_POOL_PATH;
} else {
root_path = DIST_ROOT_PATH;
url_path = format!("http://{}/{}/", DEBIAN_REPOSITORY, DEBIAN_DIST_PATH);
download_path = DEBIAN_DIST_PATH;
}
let responses = futures::stream::iter(all_paths.into_iter().map(move |path| {
let url_path = url_path.clone();
tokio::spawn(async move {
println!("{}", url_path);
})
}))
.buffer_unordered(10)
.collect::<Vec<_>>();
responses.await;
}
Side note:
If you set a variable exactly once before using it and the compiler understands that, you can skip the initialization and the mut
, like so:
async fn download_dist(all_paths: Vec<String>, mode: &str) {
let download_path;
let root_path;
let url_path;
if mode.contains("pool") {
root_path = POOL_ROOT_PATH;
url_path = format!("http://{}/{}", DEBIAN_REPOSITORY, DEBIAN_PATH);
download_path = DEBIAN_POOL_PATH;
} else {
root_path = DIST_ROOT_PATH;
url_path = format!("http://{}/{}/", DEBIAN_REPOSITORY, DEBIAN_DIST_PATH);
download_path = DEBIAN_DIST_PATH;
}
let responses = futures::stream::iter(all_paths.into_iter().map(move |path| {
let url_path = url_path.clone();
tokio::spawn(async move {
println!("{}", url_path);
})
}))
.buffer_unordered(10)
.collect::<Vec<_>>();
responses.await;
}
CodePudding user response:
You're using mut
but you don't need to. Consider:
let (download_path, url_path, root_path) = if mode.contains("pool")
{
(
POOL_ROOT_PATH,
format!("http://{}/{}",DEBIAN_REPOSITORY,DEBIAN_PATH),
DEBIAN_POOL_PATH
)
}
else
{
(
POOL_ROOT_PATH,
format!("http://{}/{}/",DEBIAN_REPOSITORY,DEBIAN_DIST_PATH),
DEBIAN_POOL_PATH
)
};
Collapsing it like this exposes that you're not really changing either download_path
or url_path
anyway.
Tip: In Rust when you have
let mut x = ""
and then later assign to it, that's a sign you might wantlet x = if (...) { }
instead.
If you're still stuck on the references, you can go down the Arc
path:
let url_path = Arc::new(url_path);
let responses = futures::stream::iter(all_paths.into_iter().map(|path| {
let url_path = url_path.clone();
tokio::spawn(async move {
println!("{}", url_path);
})
}))
This way each spawned task gets their "own" copy of the url_path
, but to avoid needless copies, they're all Arc
references to the same thing.