Как определить, какой вызов throw перехватывается в блоке catch? - PullRequest
1 голос
/ 27 марта 2020

Если в коде есть несколько мест, которые могли бы throw объект, можно ли выяснить, какой throw действительно выполняется?

x = catch(:x) do 
  throw(:x, foo) if foo
  throw(:x, bar) if bar
end

Это foo или bar (при условии, что значение недостаточно для определения)?

Если бы это было обычное исключение, можно было бы просто проверить трассировку исключения и выяснить, где оно было поднято.

Вопрос вызван отладкой гема warden, который использует throw / catch для передачи сообщения с нижних уровней промежуточного программного обеспечения на промежуточное программное обеспечение основного драгоценного камня.

Ответы [ 2 ]

2 голосов
/ 27 марта 2020

Возвращаемое значение блока catch при вызове throw является вторым аргументом throw. Если для throw не указан второй аргумент, возвращаемое значение будет nil.

Из документов для Kernel#catch:

Если бросить (tag2, val) вызывается, Ruby ищет в своем стеке блок catch, у которого tag имеет тот же object_id, что и tag2 . При обнаружении блок прекращает выполнение и возвращает val (или nil, если для throw не был задан второй аргумент.

Вот несколько примеров:

# Returns the second argument to throw
catch(:x) { throw(:x, 'foo') }
=> "foo"

# Raises exception
catch(:x) { throw(:y, 'foo') }
UncaughtThrowError: uncaught throw :y

# Returns first throw's second argument
catch(:x) do
  throw(:x, 'foo')
  throw(:x, 'bar')
end
=> "foo"

# Returns second throw's second argument
catch(:x) do
  throw(:x, 'foo') if false
  throw(:x, 'bar')
end
=> "bar"

# Skips both throws and returns the last expression in the block
catch(:x) do
  throw(:x, 'foo') if false
  throw(:x, 'bar') if false
  'baz'
end
=> "baz"

Возвращаемые значения - это строки в этих примерах, но они могут быть любого типа.

1 голос
/ 27 марта 2020

[...] возможно ли выяснить, какой бросок действительно выполняется? [...] В основе вопроса лежит отладка драгоценного камня надзирателя

Для целей отладки Ruby предоставляет Tracepoint.

В Ruby term, Kernel::throw - это обычный метод, написанный на C. Чтобы узнать, какой из них был вызван, вы можете использовать:

TracePoint.trace(:c_call) do |tp|
  printf("%s:%d\n", tp.path, tp.lineno) if tp.method_id == :throw
end

Приведенный выше код будет печатать путь к файлу и номер строки каждый раз, когда вызывается метод C с именем throw.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...