Rust has a reputation for having a steep learning curve and a rigorous and secure language. But recently, while studying The Book, I discovered that it allows variable shadowing .

If the ordinary shadowing is not enough, the same lexical scope allows different types of variables, using the same variable name .

What else can I say but shock? Let’s look at the case of rust, go.

Common usage

1
2
3
4
5
6
7
8
9
fn main() {
    let x = 5;

    let x = x + 1;

    let x = x * 2;

    println!("The value of x is: {}", x);
}

We know that the let keyword is used to name a new variable, and the second let statement creates a new variable with the name x , shadowing the first variable.

1
2
3
4
5
$ cargo run
   Compiling hello_cargo v0.1.0 (/Users/zerun.dong/code/rusttest/hello_cargo)
    Finished dev [unoptimized + debuginfo] target(s) in 1.90s
     Running `target/debug/hello_cargo`
The value of x is: 12

The final cargo run result is 12.

Advanced usage

The above example is a normal variable shadowing, although the variable name is the same, at least the value is of the same type.

1
2
3
4
5
6
7
8
9
fn main() {
    let mail = "zerun.dong@grabtaxi.com";

    println!("The value of mail is: {}", mail);

    let mail = mail.len();

    println!("The value of mail is: {}", mail);
}

In this example, the variable mail is declared to be a mailbox string, and the second let binds the variable name to an integer value.

1
2
3
4
5
6
$ cargo run
   Compiling hello_cargo v0.1.0 (/Users/zerun.dong/code/rusttest/hello_cargo)
    Finished dev [unoptimized + debuginfo] target(s) in 1.47s
     Running `target/debug/hello_cargo`
The value of mail is: zerun.dong@grabtaxi.com
The value of mail is: 23

The point is that rust actually allows variable shadowing under the same lexical scope , and the output is normal after running

What scenario would require this?

Variable shadowing has a lot of synergy with affine types (move semantics). E.g.

let foo = foo.unwrap();

where you’re rebinding foo to refer to the result of unwrap() at the same time as the old foo becomes inaccessible because of it.

The above is a statement I have seen that agrees more. It means that variable shadowing has a lot of use in affine types, and this example is mobile semantics.

For example, after rebinding a variable, the new foo is the type after unwrap, while the old foo is not imitable.

Negative example

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package main

import "fmt"

import "errors"


func fn1() error {
    return errors.New("this is fn1 error")
}

func fn2() (int, error) {
    return 0, errors.New("this is fn2 error")
}

func wrongCase(input int) (err error) {

    switch input {
    case 0:
        err = fn1()
    case 1:
        res, err := fn2()
        if err != nil {
            fmt.Println(res, err)
        }
    }
    return
}

func main(){
    fmt.Println(wrongCase(1))
}

The above is a reverse example of go variable shadowing, except in the case statement where err is the reverse case of variable shadowing

What is the output after running the code?

1
2
3
$ go run wrongccase.go
0 this is fn2 error
<nil>

The nil you see in the main, not the expected error. This is a real case that happened, very nasty …

It’s easy to fix, but how to find the problem in advance? In addition to the programmer to write more code, awareness in place, it is best to have golint infusion help, so that when the ci can be found in advance

This confirms the saying, high quality software engineering quality, need reliable tools and standard processes to ensure