-1

I don't understand how to reassign a variable in a block scope appropriately in go.

package main

import (
    "fmt"
    "path/filepath"
)

func main() {
    base := "/a/b/c"
    other := "/a/b/c/d/e"

    for base != other {
        other, file := filepath.Split(other) // "other declared but not used"
        fmt.Println(file)
    }
}

I want to use both parts of filepath.Split, so I need :=, since file is not declared yet. I want other to get shorter and shorter, so I reassign the result of filepath.Split, but the go compiler doesn't let me run this code.

Why is this the case, and how am I supposed to do this sort of thing?

samuelstevens
  • 31
  • 1
  • 5

1 Answers1

1

The body of the loop is an another block, so using short variable declaration in it will not use the other variable declared before, outside of the loop, but will create a new other variable scoped to the body block. Given that, this second other variable is not used anywhere, because the base != other condition in the loop refers to the outer other variable, hence the compile-time error.

Create the expected second variable first, and use simple assignment instead of short variable declaration:

base := "/a/b/c"
other := "/a/b/c/d/e"

for base != other {
    var file string
    other, file = filepath.Split(other) // "other declared but not used"
    fmt.Println(file)
}

Note that the above code will run into an endless loop because filepath.Split() leaves the trailing slash in other, so in the next iteration filepath.Split() will return the same other (the last dir will not be cut off), and won't ever change again.

To make your code do what you want, you have to cut trailing slashes off, like this:

for base != other {
    var file string
    other, file = filepath.Split(other) // "other declared but not used"
    fmt.Println(file)
    if strings.HasSuffix(other, "/") {
        other = other[:len(other)-1]
    }
}

This will now run and output (try it on the Go Playground):

e
d

Note that the same thing could be achieved with a lot simpler code if you'd use filepath.Base() to get the last part of the path, and filepath.Dir() to get the parent folder, like this:

base := "/a/b/c"
other := "/a/b/c/d/e"

for base != other {
    fmt.Println(filepath.Base(other))
    other = filepath.Dir(other)
}

This outputs the same, try it on the Go Playground.

See related questions:

Why does initializing just one variable in a definition fail, in golang

Why there are two ways of declaring variables in Go, what's the difference and which to use?

icza
  • 389,944
  • 63
  • 907
  • 827
  • That works, thanks. But why can't I use the short variable declaration here? – samuelstevens Oct 04 '20 at 21:24
  • @samuelstevens Added more explanation, fixed code and showed a simpler alternative. – icza Oct 04 '20 at 21:34
  • "The body of the loop is an another block, so using short variable declaration in it will not use the other variable declared before, outside of the loop, but will create a new other variable scoped to the body block. Given that, this second other variable is not used anywhere, because the base != other condition in the loop refers to the outer other variable, hence the compile-time error." This makes sense. Is it documented somewhere, or just a part of Go that I should know? – samuelstevens Oct 04 '20 at 21:36
  • Yes, it's documented: [Spec: Short variable declaration](https://golang.org/ref/spec#Short_variable_declarations): _"Unlike regular variable declarations, a short variable declaration may redeclare variables provided they were originally declared earlier in the same block (or the parameter lists if the block is the function body) with the same type, and at least one of the non-blank variables is new. As a consequence, redeclaration can only appear in a multi-variable short declaration. Redeclaration does not introduce a new variable; it just assigns a new value to the original."_ – icza Oct 04 '20 at 21:38