Яка різниця між збільшенням винятків проти викидання винятків у Ruby?


167

У Ruby є два різних механізми винятків: Throw / Catch та Raise / Rescue.

Чому у нас є дві?

Коли слід використовувати одне, а не друге?


"Вихід із вкладених циклів" є загальною потребою у багатьох мовах програмування. Окрім gotoв C / C ++, як згадував @docwhat, Java позначила перерву та продовження . (Пітон також має відхилену пропозицію щодо цього.)
Франклін Ю

Відповіді:


104

Я думаю, що http://hasno.info/ruby-gotchas-and-caveats має гідне пояснення різниці:

ловити / кидати - це не те, що піднімати / рятувати. catch / кидок дозволяє швидко виходити з блоків назад до точки, де вилов визначений для конкретного символу, порятунок підняття - це реальна обробка винятків, що стосується об'єкта Exception.


1
Цікаво дізнатись ... Читаючи це з iPad, тому не можна перевірити їх у 1.9, але деякі з цих ґатів уже не дійсні в останніх версіях рубіну, правда?
Денис де Бернарді

12
Також варто знати: raiseце дуже дорого. throwне. Подумайте, throwяк використовувати gotoдля виходу з петлі.
док.

4
@Denis Про які ви звертаєтесь?
док.

1
Посилання розірвано!
morhook

Дивіться рубіновий привід і ефективність, щоб дізнатися більше про різницю в продуктивності.
Франклін Ю

109
  • raise, fail, rescueІ ensureручки помилки , також відомий як виключення
  • throwі catchє контрольним потоком

На відміну від інших мов, кидок і ловля Рубі не використовуються для винятку. Натомість вони дають спосіб припинити виконання достроково, коли не потрібно більше подальших робіт. (Грімм, 2011 р.)

Припинення одного рівня керуючого потоку, як whileциклу, може бути виконано простим return. Припинення багатьох рівнів потоку управління, як і вкладений цикл, можна зробити за допомогою throw.

Хоча механізм виключення підйому та порятунку чудово підходить для відмови від виконання, коли справи йдуть не так, іноді приємно мати можливість вискочити з якоїсь глибоко вкладеної конструкції під час звичайної обробки. Тут дуже корисно ловити та кидати. (Томас і Хант, 2001)

Список літератури

  1. Грімм, Авді. "Кинь, лови, піднімай, рятуй ... Я так збентежений!" Блог RubyLearning. Np, 11 липня 2011 року. Веб. 1 січня 2012. http://rubylearning.com/blog/2011/07/12/throw-catch-raise-rescue--im-so-confused/ .
  2. Томас, Дейв та Ендрю Хант. "Програмування Ruby." : Посібник прагматичного програміста. Np, 2001. Веб. 29 вересня 2015. http://ruby-doc.com/docs/ProgrammingRuby/html/tut_exceptions.html .

2
Авді не схожий на те, що він звучить у подкастах.
hrdwdmrbl

2
Посилання на навчання Ruby, схоже, не працює. Ось ще одна публікація в блозі, в якій обговорюються відмінності: danielchangnyc.github.io/blog/2013/10/23/throw-raise
Dennis

Смішно, rubylearning.com вважає, що стаття Авді все ще є . Я думаю, що тому ми копіюємо речі на SO, щоб вони не були втрачені!
Джаред Бек

21

https://coderwall.com/p/lhkkug/don-t-confuse-ruby-s-throw-statement-with-raise пропонує чудове пояснення, яке я сумніваюся, що можу вдосконалитись. Підводячи підсумок, забираючи декілька зразків коду з допису в блозі:

  1. raise/ rescueє найближчими аналогами до throw/ catchконструкції, з якою ви знайомі з інших мов (або з Python's raise/ except). Якщо ви зіткнулися з умовою помилки, і ви перекладете throwїї на іншій мові, вам слід raiseв Ruby.

  2. Ruby's throw/ catchдозволяє вам порушити виконання та піднятися на стек, шукаючи catch(як raise/ ні rescue), але насправді не призначений для умов помилок. Він повинен використовуватися рідко, і чи існує лише тоді, коли catchповедінка "піднімає стек до тих пір, поки ви не знайдете відповідну " поведінка має сенс для алгоритму, який ви пишете, але не було б сенсу думати про те throw, що відповідає помилці хвороба.

    Для чого використовується Рубі? пропонує кілька пропозицій щодо приємного використання throw/ catchконструкту.

Конкретні поведінкові відмінності між ними включають:

  • rescue Fooврятує випадки Fooвключення підкласів Foo. catch(foo)тільки зловити той же об'єкт,Foo . Ви не тільки не можете передавати catchім'я класу, щоб знайти його екземпляри, але навіть не порівняєте рівності. Наприклад

    catch("foo") do
      throw "foo"
    end

    дасть вам UncaughtThrowError: uncaught throw "foo"(або ArgumentErrorв версіях Ruby до 2.2)

  • Можна перерахувати кілька пунктів порятунку ...

    begin
      do_something_error_prone
    rescue AParticularKindOfError
      # Insert heroism here.
    rescue
      write_to_error_log
      raise
    end

    тоді як декілька catches потрібно вкладати ...

    catch :foo do
      catch :bar do
        do_something_that_can_throw_foo_or_bar
      end
    end
  • Голий rescueеквівалент rescue StandardErrorі є ідіоматичною конструкцією. "Голий catch", як-от catch() {throw :foo}, ніколи нічого не піймає і не повинен його використовувати.


Добре пояснення, але задає питання, чому б на землі вони не створювали рубін = кидати іншою мовою. а потім також додайте кидок, але це! = кидок іншими мовами. Я не бачу їхньої оригінальної логіки там
wired00

@ wired00 ( знизує плечима ) Я погоджуюся, що це здається досить ексцентричним порівняно з іншими популярними мовами сьогодні.
Марк Амері

2
@ wired00: його називають "підвищенням" винятку ще з перших експериментів із структурованим поводженням з помилками у 1960-х, його називають "підняттям" винятком у стаціонарних статтях, що винайшли сучасну форму обробки винятків, її називають "підняття" виключення в Lisps and Smalltalks, які були однією з головних натхненників для Ruby, і це називається "підвищення" винятком або "підвищення" перерви в апаратному забезпеченні, де концепція існувала ще до концепції "програмування" мова »існувала. Питання має бути швидше: чому ці інші мови змінили це?
Йорг W Міттаг

@MarkAmery: Пам'ятайте, що багато хто з "інших популярних мов" є молодшими за Рубі або принаймні сучасними. Отже, питання швидше має бути: чому ті інші мови не слідували за Ruby (і Smalltalk, і Lisp, і апаратне забезпечення, і література).
Йорг W Міттаг

@ JörgWMittag Цікаво - ти надихнув мене на невелике історичне дослідження. C ++ мав поняття "кидати" виняток за роки до того, як Рубі з'явився, і згідно english.stackexchange.com/a/449209/73974 термін насправді повертається до 70-х років ... тому я думаю, що ми все ще можемо критикувати Рубі за взяти усталену термінологію та використовувати її для позначення чогось зовсім іншого.
Марк Амері
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.