In Rust we must understand the concept of moved values, and how to prevent values from moving when not wanted. A program will not even compile with this kind of problem.
In this language, a value like a string
can be moved into a Vector
, loop, or function. To prevent moving, we can call to owned()
or create Arc
references.
This program shows how we might get into an error with moved values. We create the "shared" string
and pass it to the push()
function on a vector.
string
to another vector. But this will not compile.string
was moved into the "elements" vector, so we cannot use it in the "elements2" vector.string
into the first vector.fn main() { let shared = "bird".to_uppercase(); let mut elements = vec![]; elements.push(shared); // Value "shared" was moved here. // let mut elements2 = vec![]; // elements2.push(shared); // Cannot reuse the "shared" value here. println!("DONE"); }DONE
The Rust compiler carefully explains the problem in the program. The string
does not copy itself automatically, as it lacks the Copy trait.
string
is moved into the first vector. But this causes the string
to not be available for use in the second vector.error[E0382]: use of moved value: shared move occurs because shared has type String, which does not implement the Copy trait ... value moved here ... value used here after move
We can copy the string
into the first vector, which prevents it from moving there. We can use the "to owned" function to copy and transfer ownership.
string
to be copied, so it exists in 2 places.fn main() { let shared = "bird".to_uppercase(); let mut elements = vec![]; elements.push(shared.to_owned()); // Copy into this vector, without moving. let mut elements2 = vec![]; elements2.push(shared); // Move the element here. println!("DONE"); }DONE
Arc
exampleSuppose we want to share the same string
in the 2 vectors. We can do this by creating an Arc
with the string
as an argument. Then we clone()
the Arc
instead of copying a string
.
Arc
is an atomic reference count. This means is can be used across threads—an Rc
is a single-threaded, faster version.Arc
. If the vectors were stored in a struct
, or passed to a function, we would need to change the type.use std::sync::Arc; fn main() { let shared = "bird".to_uppercase(); let arc = Arc::new(shared); let mut elements = vec![]; elements.push(arc.clone()); // Copy Arc into vector. let mut elements2 = vec![]; elements2.push(arc.clone()); println!("DONE: {}", elements[0]); }DONE: BIRD
Consider a short string—one with just 3 characters. This would be something we could copy to multiple vectors without any performance worries.
string
, like 1000 characters, we would benefit from sharing the data and using Arc
references to it.For new Rust developers, it is easiest to just copy everything more than needed. Later, we can optimize out copies by adding Arcs to the data that is large enough to be a concern.