0

I wanted to know if rust is doing variable shadowing under the hood when variables move from one scope to another.

Since when I move a variable to another function the other function's signature can be changed to make the variable mutable and but when the variable is moved/returned back from other function to main it's not immutable anymore.

I am just curious to what's happening here.

fn main() { let vec0 = Vec::new(); // immutable by default let mut vec1 = foreign_func(vec0); // vec0 has now moved // Is foreign_func returning a mutable vector? Because without `mut` keyword vec1 is immutable by default. vec1.push(10); } fn foreign_func(mut vec: Vec<i32>) -> Vec<i32> { // argument passed for vec is now mutable in this scope. // Is this like variable shadowing? // let test = 10; // let mut test = 20; vec.push(20); vec } 
2
  • 2
    The owner of a variable can always mutate it: the privilege of being the owner is that even if a binding is not mutable, you can move the value - including into a mutable binding. Shadowing is a different idea: there can be multiple bindings in the same scope with the same name, but only the most recently declared one is accessible. Commented May 23, 2021 at 20:45
  • @PeterHall I can somewhat understand foreign_func being able to mutate vec but then when vec is moved back to main it's no more mutable. So vec was passed as immutable by rust internally? Commented May 24, 2021 at 8:05

1 Answer 1

4

There's no such thing as an immutable Vec, it's only a variable or a borrow that can be immutable (actually unique/exclusive, but in case of Vec that's the same thing).

What happens in your example is a move: the Vec first moves from main's vec0 into foreign_func's vec, and then back to main, this time to vec1. Some of these bindings are mut and some are not, but that has no bearing on the moved value. In fact, transferring a Vec from an immutable to a mutable variable can be as simple as let mut vec1 = vec0, which compiles just fine.

As pointed out by Peter Hall, shadowing is a different concept: introducing a new variable which intentionally shares the name of an existing one. Since the two variables have the same name, only the most recent one is accessible in its scope. Note that the shadowed variable and the one that shadowed it don't have to have the same type, and that a move may or may not occur when shadowing:

fn shadow1(v: Option<Vec<u32>>) { // here shadowing moves the old value let v = v.unwrap_or_else(Vec::new); } fn shadow2(v: Vec<u32>) { // here shadowing not accompanied by move let v = v.as_slice(); } 

When there is no move, it is possible that the shadowed value will again become useful after an operation ends. In that case the shadowing variable may be introduced in a separate scope, so that the old value resurfaces after the inner scope finishes:

fn shadow3(v: Vec<u32>) { { let v = v.as_slice(); // temporarily working with a &[u32] slice, Vec is inaccessible } // here we have the Vec again } 
Sign up to request clarification or add additional context in comments.

2 Comments

I can somewhat understand foreign_func being able to mutate vec but then when vec is moved back to main it's no more mutable. So vec was passed as immutable by rust internally?
@SukhinderpalMann As I wrote in the answer: there's no such thing as an immutable Vec. Every value is itself mutable, only a variable or reference can be immutable. So "passed as immutable" has no meaning in Rust.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.