It's adding to the matrix of results.
Using this similar but more elegant version
perm <- function(v) {
n <- length(v)
if (n == 1) v
else {
X <- NULL
for (i in 1:n) X <- rbind(X, cbind(v[i], perm(v[-i])))
X
}
}
perm(letters[1:3])
#> [,1] [,2] [,3]
#> [1,] "a" "b" "c"
#> [2,] "a" "c" "b"
#> [3,] "b" "a" "c"
#> [4,] "b" "c" "a"
#> [5,] "c" "a" "b"
#> [6,] "c" "b" "a"
Created on 2020-10-18 by the reprex package (v0.3.0.9001)
if traps for a single element vector
for (i in 1:n) X <- rbind(X, cbind(v[i], perm(v[-i])))
populates a matrix by iterating over the length of the vector and taking its element at each step and creating a row in X (initially NULL) with the value of that element and, the elegant part, recursively calling itself to create the columns with the remaining possibilities with the elements less that that which starts the row.