I'm attempting to optimize an application through smart cloning and borrowing, and I'm observing the following behavior. The program below wouldn't work:
fn f( string: String) { println!("{}", string ); } fn main() { let my_string: String = "ABCDE".to_string(); f( my_string ); f( my_string ); } It generates the well-known "used after move" error.
7 | f( my_string ); | --------- value moved here 8 | f( my_string ); | ^^^^^^^^^ value used here after move This can be solved by cloning my_string. The program below works fine:
fn f( string: String) { println!("{}", string ); } fn main() { let my_string: String = "ABCDE".to_string(); f( my_string.clone() ); f( my_string.clone() ); } However, if you use the same approach in a multi-threaded environment, cloning doesn't help any longer. When the function calls are embedded in threads:
use std::thread; fn f( string: String) { println!("{}", string ); } fn main() { let my_string: String = "ABCDE".to_string(); thread::spawn( move || { f( my_string.clone() ); } ); thread::spawn( move || { f( my_string.clone() ); } ); } the program generates the "used after move" error again:
10 | thread::spawn( move || { f( my_string.clone() ); } ); | ^^^^^^^ --------- use occurs due to use in closure | | | value used here after move However, you can remedy this by moving the thread into the function, with the same net effect:
use std::thread; fn f( string: String) { thread::spawn( move || { println!("{}", string ); } ); } fn main() { let my_string: String = "ABCDE".to_string(); f( my_string.clone() ); f( my_string.clone() ); } The above program works fine. Or, if you prefer, you can clone my_string in advance and use the clone in the second function call:
use std::thread; fn f( string: String) { println!("{}", string ); } fn main() { let my_string: String = "ABCDE".to_string(); let my_second_string: String = my_string.clone(); thread::spawn( move || { f( my_string.clone() ); } ); thread::spawn( move || { f( my_second_string ); } ); } It looks a little like trial and error, but some theory can perhaps explain it.
There is another question regarding the "used after move" error. The other question discusses the effect of to_string(), while this one discusses clone() in a threaded environment.
clone/moveidiom this way:thread::spawn({ let my_string = my_string.clone(); move || { f( my_string ); } });.