Let’s say you have a piece of code:
class SomeThing
def initialize(thing)
@thing = thing
end
def call
puts @thing.inspect
end
end
You want to execute this in parallel in a ractor:
Ractor.new { SomeThing.new(1).call }
This works if you have the block written within the Ractor. But it will fail if the block is refferenced from the outside of a ractor.
element = proc { SomeThing.new(1).call }
Ractor.new { element.call }
# <internal:ractor>:267:in `new': can not isolate a Proc because it accesses
# outer variables (element). (ArgumentError)
To do that you need to instance_eval
from a shareable object:
class Worker
def initialize(&block)
@block = block
end
def run
block = @block
Ractor.current.instance_eval { Ractor.new(&block) }
end
end
worker = Worker.new { SomeThing.new(1).call }
worker.run.take
Important Link to heading
The Ractor.current.instance_eval
usage changes the value of self
within the
block, and you can not reference variables implicitly.
one = 1
worker = Worker.new { SomeThing.new(one).call }
worker.run.take
# <internal:ractor>:267:in `new': can not isolate a Proc because it accesses
# outer variables (one). (ArgumentError)
So make sure you pass all dependencies through the Ractor constructor:
class Worker
def initialize(*args, &block)
@args = args
@block = block
end
def run
args = @args
block = @block
Ractor.current.instance_eval { Ractor.new(*args, &block) }
end
end
one = 1
worker = Worker.new(one, 2, 3) { |*args| SomeThing.new(args).call }
worker.run.take