fn main() {
let x = 5;
println!("{}", x);
let x = 3.14;
println!("{}", x);
let x = "Hello";
println!("{}", x);
}
What happens to the previous values? My understanding is that they are not destroyed.
Is there a way to still access these values?
If they are still consuming memory space, is it possible to release that space?
- What happens to the previous values?
Nothing.
- Is there a way to still access these values?
No.
- If they are still consuming memory space, is it possible to release that space?
No.
Now, all of the above should be true from the perspective of the code. They're not necessarily true from the perspective of the optimiser. For example, the optimiser might notice that uses of the first and second x
's don't overlap, so it can re-use the stack storage of the first x
for the second x
.
Or it might not. The language itself, insofar as I am aware, has no opinion on the subject.
The only way I'm aware of that you can guarantee that a local variable will definitely release it's stack storage is to return from the function it's defined in.
... well, until you consider inlining, which could also make that not true.
Short version: don't worry about it, unless you're using so much stack space that it's causing actual, measurable problems.
The value can be moved out before the variable is shadowed, but ultimately the value can't be accessed from a shadowed variable.
Answer to Q1: The decision is up to the compiler and data type and size and OS and system memory load (usually stack based data types stay to the end of the main
, and heap based data types with big memory footprint may need to drop if needed).
Answer to Q2: After shadowing: No, before shadowing: Yes (Values), run this code.
Answer to Q3: After shadowing: No, before shadowing: see: Disposes of a value. and Rust manual memory management
Variable scoping and shadowing:
Advantages:
1. Since data cannot be accessed from outer scope, Data Integrity is preserved.
2. When "we need more alphabets", this is nice way to limit variables scope.
Also this works well when you need more local variables or scope.
A way to still access these values before shadowing (note: move forced the closure to take ownership of 'x'):
use std::{thread, time};
fn main() {
let mut v = vec![];
let d = time::Duration::from_millis(100);
let x = 5;
println!("{}", x);
v.push(thread::spawn(move || {
for _ in 1..10 {
thread::sleep(d);
println!("Thread 1: {}", x);
}
}));
let x = 3.14;
println!("{}", x);
v.push(thread::spawn(move || {
for _ in 1..10 {
thread::sleep(d);
println!("Thread 2: {}", x);
}
}));
let x = "Hello";
println!("{}", x);
v.push(thread::spawn(move || {
for _ in 1..10 {
thread::sleep(d);
println!("Thread 3: {}", x);
}
}));
for child in v {
let _ = child.join();
}
}
output:
5
3.14
Hello
Thread 1: 5
Thread 3: Hello
Thread 2: 3.14
Thread 2: 3.14
Thread 3: Hello
Thread 1: 5
Thread 1: 5
Thread 3: Hello
Thread 2: 3.14
Thread 2: 3.14
Thread 3: Hello
Thread 1: 5
Thread 2: 3.14
Thread 3: Hello
Thread 1: 5
Thread 2: 3.14
Thread 1: 5
Thread 3: Hello
Thread 2: 3.14
Thread 1: 5
Thread 3: Hello
Thread 3: Hello
Thread 2: 3.14
Thread 1: 5
Thread 1: 5
Thread 2: 3.14
Thread 3: Hello
Note: move
forces the closure to take ownership of 'x', so the address of local x
is not the same as thread x
but the value is:
use std::thread;
fn main() {
let mut v = vec![];
let x = 5;
println!("{:p}", &x);
v.push(thread::spawn(move || {
println!("Thread 1: {:p}", &x);
}));
let x = 3.14;
println!("{:p}", &x);
v.push(thread::spawn(move || {
println!("Thread 2: {:p}", &x);
}));
let x = "Hello";
println!("{:p}", &x);
v.push(thread::spawn(move || {
println!("Thread 3: {:p}", &x);
}));
for child in v {
let _ = child.join();
}
}
output:
0x8bf934
0x8bf9b8
0x8bfa40
Thread 1: 0x4a3faec
Thread 2: 0x4c3fae8
Thread 3: 0x4e3fa70
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With