zxdvd
zxdvd

Reputation: 458

strange behaviour while same variable as input and output of function

For the following code, It seems going into dead loop. After s, err := minusOne(s), s is shorten according the log info. But the before minus log shows that it never changed.

func minusOne(s string) (string, error) {
    if len(s) >= 0 {
        return s[1:], nil
    }
    return "", nil
}

func TestStr(t *testing.T) {
    s := "hello world"
    for {
        log.Println("before minus", s)
        s, err := minusOne(s)
        log.Println("after minus", s)
        if err == nil && len(s) == 0 {
            break
        }
    }
}

If I change it slightly like, it works like expected.

s1, err := minusOne(s)
s = s1

Or if I remove the err returning in the minusOne function, and it also works.

s = minusOne(s)

I really can't understand it. Anyone can help with it?

Upvotes: 1

Views: 65

Answers (3)

skovorodkin
skovorodkin

Reputation: 10264

On each iteration you declare new variables called s and err:

s, err := minusOne(s)

Their scope is only the current iteration, so the above line keeps using the same s variable declared outside of the loop. From Declarations and scope part of The Go Specification:

Go is lexically scoped using blocks:

  1. The scope of a constant or variable identifier declared inside a function begins at the end of the ConstSpec or VarSpec (ShortVarDecl for short variable declarations) and ends at the end of the innermost containing block.

To fix it, you need to use assignment:

var err error
for {
    // ...
    s, err = minusOne(s)
    // ...
}

Upvotes: 5

Ivan Beldad
Ivan Beldad

Reputation: 2371

You are using the short var declaration :=. You must take care with this.

Inside the for loop you are creating two new variables, err, but also s (with a new scope inside the for loop). This wouldn't be a problem if you do in the same scope.

You can simply extract the err to a pre-declared variable before like that:

var err error
s, err = minusOne(s)

Upvotes: 3

shanyy
shanyy

Reputation: 86

do not use s in the for loop, := will add a new local var. just rename the s in for loot to s1. You can also declare a outside for loop and replace := with =

Upvotes: 1

Related Questions