I am new to Rust, and I am having a hard time wrapping my head around some of the reference/borrow behavior.
Here is a snippet which does not compile. I am attempting to create an array of u64
"slices", whose values I want to modify, and then access later:
fn main() {
// Initialize an array of "slices" with dummy data
let mut refs: [&[u64]; 4] = [&[0; 2]; 4];
// Write some computed values to the array
for i in 0..refs.len() {
// ...
// some computation to get `x` and `y`
// ...
let data: [u64; 2] = [x as u64, y as u64];
let data_ref: &[u64] = &data;
refs[i] = data_ref;
}
// Read the computed values from the array
println!("{}", refs[3][0]);
}
The compiler returns:
error[E0597]: `data` does not live long enough
--> src\main.rs:8:32
|
7 | let data: [u64; 2] = [x as u64, y as u64];
| ---- binding `data` declared here
8 | let data_ref: &[u64] = &data;
| ^^^^^ borrowed value does not live long enough
9 | refs[i] = data_ref;
10 | }
| - `data` dropped here while still borrowed
...
13 | println!("{}", refs[3][0]);
| ------- borrow later used here
While I understand that data
is scoped to live within the for
loop, and that it will no longer exist outside of the for
loop (so setting refs[i]
to reference data
should result in a compiler error since refs
outlives data
), I am unsure of an alternative way to correctly set values of refs
.
What is the appropriate ("rusty") way to declare, initialize, then read "slice" values from an array like this?
The problem is slices reference data that lives "somewhere else". They also can't really outlive the data they reference.
If you define
let data: [u64; 2] = [x as u64, y as u64];
in your loop then data
dies with each iteration. Therefore
you can't have
let data_ref: &[u64] = &data;
refs[i] = data_ref;
as, when the loop finishes, it would make refs
contain a reference to a piece of data that doesn't exist anymore (data
).
In your example you can replace slices with 2-element arrays, like so:
fn main() {
let mut arrays = [[0; 2]; 4];
// Write some computed values to the array
for i in 0..arrays.len() {
// ...
// some computation to get `x` and `y`
// ...
let x = 1;
let y = 2;
arrays[i] = [x, y];
}
// Read the computed values from the array
println!("{}", arrays[3][0]);
}
the the code prints 1
.
If you truly need an array of slices (for example when you get refs
from the outside) I think you likely need copy the data into the slices, like so:
(Edit: added the explicit [&mut [u64]; 4]
type after drewtato's note)
fn main() {
// Initialize an array of "slices" with dummy data
// The array doesn't need to be mutable (we don't modify it)
// The slices have to be mutable (we modify them)
let refs: [&mut [u64]; 4] = [&mut [0; 2], &mut [0; 2], &mut [0; 2], &mut [0; 2]];
// Write some computed values to the array
for i in 0..refs.len() {
// ...
// some computation to get `x` and `y`
// ...
let x = 1;
let y = 2;
refs[i].copy_from_slice(&[x, y]);
}
// Read the computed values from the array
println!("{}", refs[3][0]);
}
(I have this ugly [&mut [0; 2], &mut [0; 2], &mut [0; 2], &mut [0; 2]]
form in there because [&mut [0; 2]; 4]
won't work – mutable array references aren't Copy
so it's not so easy to initialize refs
that way).
See copy_from_slice
documentation for details.