When you define a loop for X in ..., you're defining scope that exists during each iteration and a constant that exists in that scope. I can't find explicit documentation of scope except for in the case of do {} but the documentation does assume scope is created and destroyed in loops when speaking of things like the guard declaration. In practice, this is what the scope for i in a single loop looks like.
// Scope that i doesn't exist in.
for i in 1..<2 {
// Scope that i exists in.
}
// Scope that i doesn't exist in.
When defining a nested scope (which your inner for loop is), you have the ability to redefine variables and constants. This is what happens when you define i in the loop declaration. Inside the inner loop, the compiler looks outward through the scope for definitions.
In the case of i, it looks in the inner loop and finds it. In the case of print, the identifier used, it looks in the inner loop, the outer loop, any containing fiction, then class, then module, then imported modules until it finds Swift.print.
for let iwhich makes that more obvious.