Go function to generate permutations of a given integer array
To make it nicer for your reviewers you could post as a complete and
runnable example:

package main

import "fmt"

...

func main() {
    input := []int{1, 2, 3}
    output := permute(input)
    fmt.Printf("Input: %v\nOutput:\n", input)
    for _, x := range output {
        fmt.Printf("  %v\n", x)
    }
}




With that out of the way, I'd simplify permute a bit:

func permute(nums []int) [][]int {
    return backtrack(nums, []int{}, make([]bool, len(nums)), [][]int{})
}


In backtrack I'd consider returning early for the first case, that
lets you remove one level of indentation for the rest:

func backtrack(nums []int, tmp []int, invalid_pos []bool, res [][]int) [][]int {
    if len(tmp) == len(nums) {
        tmp2 := make([]int, len(tmp))
        copy(tmp2, tmp)
        return append(res, tmp2)
    }
    ...
}




Regarding your question:  Well, what happens if you remove the copy
call?

0 go % go run perm.go
Input: [1 2 3]
Output:
  [0 0 0]
  [0 0 0]
  [0 0 0]
  [0 0 0]
  [0 0 0]
  [0 0 0]


Clearly it does something and you cannot remove it just like that.  It
also has nothing to do with capturing iteration variables, the only ones
you have here are i and num - both aren't even part of the copy
call, plus, you're not capturing anything, there's no func() {...}
declaration anywhere that even could capture variables.

Variable capturing and the (most common?) problem with it comes only if you have a construction like this:

for i := ... {
    x := func() {
        foo(i)
    }
}


The i in the anonymous function is the one from the loop - and since it's always the same variable, all the created functions reference ... the same variable!  That can be confusing since people might expect that on each iteration of the loop we're capturing that one value that i currently has.  Go could've done that, but I'm guessing mostly for performance reasons that's not being done and you've to explicitly make it happen yourself:

for i := ... {
    j := i
    x := func() {
        foo(j)
    }
}


Now foo gets called with all the different values of i.



Okay so that's that, now I'd just recommend simplifying this algorithm
to get rid of invalid_pos, tmp and the res parameter and do it all
functionally and recursively.  I'm saying that because the code right
now is pretty complex for the problem and yet it doesn't do everything
upfront that it could, like preallocating the res array to the final
length, so it seems this is more of a learning exercise.

Start with the base cases, empty list and one element, then consider the
one element longer list:

func permute2(nums []int) [][]int {
    if len(nums) &lt;= 1 {
        return [][]int{nums}
    }
    result := [][]int{}
    for i, x := range nums {
        ...
    }
    return result
}


Don't optimise to early for reusing things, that can also follow once
the algorithm is in its most simple form; clarity comes first.

Hint:  You can do it without copy or manually instantiating any
more slices via []int{}, just check out
append and how to do
subslices.  Take particular care to not simply append slices together without considering how the underlying data is going to be shared (that's why append([]int{}, &lt;slice here&gt;) might be necessary to prevent the slice from being modified).



As a spoiler:


   func permute2(nums []int) [][]int {
     if len(nums) &lt;= 1 {
         return [][]int{nums}
     }
     result := [][]int{}
     for i, x := range nums {
         without := append(append([]int{}, nums[:i]...), nums[i+1:]...)
         for _, y := range permute2(without) {
             result = append(result, append([]int{x}, y...))
         }
     }
     return result
 }