In this lesson we’re going to learn how to share references to the data in our variables instead of copying the data.
❯ cargo run post rust,bevy "Doing shader stuff in Bevy" doing-shader-stuff-in-bevy.md
Compiling first v0.1.0 (/rust-adventure/first-cli)
Finished dev [unoptimized + debuginfo] target(s) in 0.18s
Running `target/debug/first post rust,bevy 'Doing shader stuff in Bevy' doing-shader-stuff-in-bevy.md`
[src/main.rs:5] &args = [
"target/debug/first",
"post",
"rust,bevy",
"Doing shader stuff in Bevy",
"doing-shader-stuff-in-bevy.md",
]
[src/main.rs:12] layout = "post"
[src/main.rs:12] tags = "rust,bevy"
[src/main.rs:12] heading = "Doing shader stuff in Bevy"
[src/main.rs:12] filename = "doing-shader-stuff-in-bevy.md"
The Code
We’re going to end up with this code.
use std::env;
fn main() {
let args: Vec<String> = env::args().collect();
dbg!(&args);
let layout = &args[1];
let tags = &args[2];
let heading = &args[3];
let filename = &args[4];
dbg!(layout, tags, heading, filename);
}
Borrowing
In the case of our CLI, cloning works just fine: the extra copies of data don’t matter from a performance perspective.
Instead I want to introduce the concept of borrowing as a way to convey information about your program and have the Rust compiler enforce that information.
You can imagine that the implementation of dbg!
, for example, isn’t going to need to mutate the value we give it. It’s going to print the data to the console, not append characters to the string.
Similarly, our argument variables don’t need to own their data. They can use read-only access to the data we already have stored in args
.
Shared References
Using shared references, we can explicitly state that the places we’re using the data are only going to read the data and not mutate it.
This means that we can have as many shared references to the original Vec
as we want since every usage can read the data without worrying about what the rest of our program is doing.
Everywhere we used .clone()
, we can replace that usage with &
before the value.
use std::env;
fn main() {
let args: Vec<String> = env::args().collect();
dbg!(&args);
let layout = &args[1];
let tags = &args[2];
let heading = &args[3];
let filename = &args[4];
dbg!(layout, tags, heading, filename);
}
&
is how we create shared references to the original data instead of creating copies of it.
dbg!
gets a shared reference to the entire Vec
of String
s.
layout
, tags
, heading
, and filename
all get shared references to a specific String
at an index in the Vec
.
When to clone and when to borrow?
In general, you should clone data when you want copies of it and borrow data when you want to share access to some data.
In practice knowing when you need to clone and when it makes sense to borrow is something that will take writing code in different situations.
If you run into trouble, you can often get away with using .clone to get you out of a jam if you’re feeling overwhelmed by the compiler feedback.