Why pass variable into fiber(spawn) in a loop not work?

i = 0
while i < 10
  _i = i
  spawn do
    puts(_i)   # <= the variable _i is not a shared variable, right?
  end
  i += 1
end

Fiber.yield

On my laptop, the output is:

 ╰─ $ 1  cr 1.cr 
9
9
9
9
9
9
9
9
9
9

See Concurrency - Crystal.

while does not create a new scope. So _i is closured and shared within the entire top-level scope.

1 Like

A block creates a new scope, so this program would have the desired effect:

10.times do |i|
  _i = i
  spawn do
    puts(_i)
  end
end
Fiber.yield

EDIT: I removed an completely unnecessary begin ... end block which wasn’t supposed to be in the example.

1 Like

That’s why you can also do spawn puts(i)

It does all the boilerplate that @straight-shoota just typed.

1 Like

There’s also a section in the reference book that I linked to that explains/suggests using spawn puts(i). Tho it seems my link gets collapsed down to make it look like i’m just linking to the concurrency docs suggesting “go look it up yourself” :sweat_smile:.

Thanks, @Blacksmoke16 , in fact, I have read these documents before.

what i expected answer is @straight-shoota 's this answer, it made me understand why.

i just remember that in some languages, golang? when assign the loop pushed variable into a local variable, can avoid this issue, that is way i add a _i = i within the while loop, there is no example in the linked document explain this.

Actually Go has the same issue: redefining for loop variable semantics · Discussion #56010 · golang/go · GitHub

With a block you don’t need to assign the variable to another variable. That’s another advantage of blocks. No need for that _i in your example.

I guess another advise is: try to use idiomatic code like looping over ranges, 10.times, etc. Try to avoid while, especially because there are many abstractions over it. While can be considered a low level primitive.

2 Likes

Yes, in fact, following code works.

10.times do |i|
  spawn do
    puts(i)
  end
end
Fiber.yield

But, following code not work. because begin ... end not create a new scope too.

i = 0
while i < 10
  begin
    _i = i
    spawn do
      puts(_i)
    end
    i += 1
  end
end

Fiber.yield