Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Variable shadowing in the same scope in Rust?

fn main() {
    let x = 5;
    println!("{}", x);

    let x = 3.14;
    println!("{}", x);

    let x = "Hello";
    println!("{}", x);
}
  1. What happens to the previous values? My understanding is that they are not destroyed.

  2. Is there a way to still access these values?

  3. If they are still consuming memory space, is it possible to release that space?

like image 223
r_duck Avatar asked Apr 22 '18 03:04

r_duck


2 Answers

  1. What happens to the previous values?

Nothing.

  1. Is there a way to still access these values?

No.

  1. 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.

like image 58
DK. Avatar answered Oct 08 '22 23:10

DK.


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
like image 41
wasmup Avatar answered Oct 09 '22 00:10

wasmup