Sự khác biệt giữa Tăng ngoại lệ so với ném ngoại lệ trong Ruby là gì?


167

Ruby có hai cơ chế ngoại lệ khác nhau: Ném / Bắt và Nâng / Cứu.

Tại sao chúng ta có hai?

Khi nào bạn nên sử dụng cái này chứ không phải cái kia?


Ra khỏi vòng lặp lồng nhau là một nhu cầu phổ biến trong nhiều ngôn ngữ lập trình. Bên cạnh việc gototrong C / C ++ như @docwhat đã đề cập, Java đã dán nhãn break và continue . (Python cũng có một đề xuất bị từ chối cho việc này.)
Franklin Yu

Câu trả lời:


104

Tôi nghĩ rằng http://hasno.info/ruby-gotchas-and-caveats có một lời giải thích hợp lý về sự khác biệt:

bắt / ném không giống như nâng / cứu. bắt / ném cho phép bạn nhanh chóng thoát khỏi các khối trở lại điểm mà một điểm bắt được xác định cho một biểu tượng cụ thể, nâng cao cứu hộ là công cụ xử lý ngoại lệ thực sự liên quan đến đối tượng Ngoại lệ.


1
Tò mò muốn biết ... Đọc cái này từ iPad, vì vậy không thể kiểm tra chúng trong 1.9, nhưng một số trong số các vấn đề đó không còn hiệu lực trong các phiên bản ruby ​​gần đây, phải không?
Denis de Bernardy

12
Cũng đáng biết: raiserất tốn kém. throwkhông phải. Hãy nghĩ về throwviệc sử dụng gotođể thoát khỏi một vòng lặp.
docwhat

4
@Denis Bạn đang đề cập đến vấn đề gì?
docwhat

1
Liên kết bị hỏng!
morhook

Xem ruby Catch-throw và hiệu quả để hiểu thêm về sự khác biệt hiệu suất.
Franklin Yu

109
  • raise, fail, rescue, Và ensurexử lý sai sót , hay còn gọi là trường hợp ngoại lệ
  • throwcatchdòng điều khiển

Không giống như trong các ngôn ngữ khác, ném và bắt của Ruby không được sử dụng cho các trường hợp ngoại lệ. Thay vào đó, họ cung cấp một cách để chấm dứt thực hiện sớm khi không cần làm thêm. (Grimm, 2011)

Việc chấm dứt một cấp điều khiển duy nhất, như một whilevòng lặp, có thể được thực hiện một cách đơn giản return. Chấm dứt nhiều cấp độ của luồng điều khiển, như một vòng lặp lồng nhau, có thể được thực hiện với throw.

Mặc dù cơ chế ngoại lệ của nâng và cứu là tuyệt vời để từ bỏ thực thi khi có sự cố, đôi khi thật tuyệt khi có thể nhảy ra khỏi một số cấu trúc được lồng sâu trong quá trình xử lý thông thường. Đây là nơi bắt và ném có ích. (Thomas và Hunt, 2001)

Người giới thiệu

  1. Grimm, Avdi. "Ném, bắt, nâng, giải cứu tôi rất bối rối!" Blog của RubyLearning. Np, ngày 11 tháng 7 năm 2011. Web. Ngày 1 tháng 1 năm 2012. http://rubylearning.com/blog/2011/07/12/throw-catch-raise-resTHER--im-so- bối rối / .
  2. Thomas, Dave và Andrew Hunt. "Lập trình Ruby." : Hướng dẫn lập trình viên thực dụng. Np, 2001. Web. Ngày 29 tháng 9 năm 2015. http://ruby-doc.com/docs/ProgrammingRuby/html/tut_exceptions.html .

2
Avdi không giống như âm thanh của anh ấy trong podcast.
hrdwdmrbl

2
Liên kết Ruby Learning dường như không hoạt động. Đây là một bài đăng trên blog khác nói về sự khác biệt: danielchangnyc.github.io/blog/2013/10/23/throw-raise
Dennis

Thật buồn cười, rubylearning.com nghĩ rằng bài báo của Avdi vẫn còn đó . Tôi đoán đó là lý do tại sao chúng tôi sao chép nội dung sang SO, vì vậy nó sẽ không bị mất!
Jared Beck

21

https://coderwall.com/p/lhkkug/don-t-confuse-ruby-s-throw-statement-with-raise cung cấp một lời giải thích tuyệt vời mà tôi nghi ngờ tôi có thể cải thiện. Để tóm tắt, đặt biệt danh cho một số mẫu mã từ bài đăng trên blog khi tôi đi:

  1. raise/ rescuelà những từ tương tự gần nhất với throw/ catchxây dựng mà bạn quen thuộc với các ngôn ngữ khác (hoặc với Python's raise/ except). Nếu bạn gặp phải một điều kiện lỗi và bạn sẽ khắc throwphục nó bằng ngôn ngữ khác, bạn nên sử dụng raiseRuby.

  2. Ruby's throw/ catchcho phép bạn phá vỡ sự thực thi và trèo lên ngăn xếp tìm kiếm catch(như raise/ rescuekhông), nhưng thực sự không có nghĩa đối với các điều kiện lỗi. Nó nên được sử dụng hiếm khi và chỉ có khi hành vi "đi lên ngăn xếp cho đến khi bạn tìm thấy một catchhành vi tương ứng " có ý nghĩa đối với thuật toán bạn đang viết nhưng sẽ không có ý nghĩa gì khi nghĩ về throwlỗi tương ứng với lỗi tình trạng.

    Bắt và ném được sử dụng trong Ruby là gì? cung cấp một số gợi ý về việc sử dụng tốt đẹp của throw/ catchxây dựng.

Sự khác biệt hành vi cụ thể giữa chúng bao gồm:

  • rescue Foosẽ giải cứu các trường hợp Foobao gồm các lớp con của Foo. catch(foo)sẽ chỉ bắt cùng một đối tượngFoo ,. Bạn không chỉ không thể vượt qua catchmột tên lớp để bắt các trường hợp của nó, mà thậm chí nó sẽ không thực hiện so sánh bình đẳng. Ví dụ

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

    sẽ cung cấp cho bạn UncaughtThrowError: uncaught throw "foo"(hoặc một ArgumentErrorphiên bản Ruby trước 2.2)

  • Nhiều điều khoản giải cứu có thể được liệt kê ...

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

    trong khi nhiều catches cần được lồng ...

    catch :foo do
      catch :bar do
        do_something_that_can_throw_foo_or_bar
      end
    end
  • Một trần rescuelà tương đương rescue StandardErrorvà là một cấu trúc thành ngữ. Một "trần catch", như catch() {throw :foo}, sẽ không bao giờ bắt được bất cứ thứ gì và không nên được sử dụng.


Giải thích tốt nhưng đặt ra câu hỏi, tại sao trên trái đất họ sẽ thiết kế nâng cao trong ruby ​​= ném vào ngôn ngữ khác. và sau đó cũng bao gồm ném nhưng nó! = ném trong các ngôn ngữ khác. Tôi không thể thấy logic ban đầu của họ ở đó
Wired00

@ Wired00 ( Shrug .) Tôi đồng ý rằng nó có vẻ khá lập dị so với các ngôn ngữ phổ biến khác hiện nay.
Đánh dấu Amery

2
@ Wired00: Nó được gọi là "nâng cao" một ngoại lệ kể từ những thí nghiệm đầu tiên xử lý lỗi có cấu trúc vào những năm 1960, nó được gọi là "nâng cao" một ngoại lệ trong các bài báo chuyên đề phát minh ra hình thức xử lý ngoại lệ hiện đại, nó được gọi là "nâng cao" một ngoại lệ trong Lisps và Smalltalks, đó là một trong những nguồn cảm hứng chính cho Ruby, và nó được gọi là "nâng cao" một ngoại lệ hoặc "nâng cao" một sự gián đoạn trong phần cứng, trong đó khái niệm tồn tại ngay cả trước khi có khái niệm về "lập trình" ngôn ngữ "đã tồn tại. Câu hỏi nên là: tại sao những ngôn ngữ khác thay đổi điều đó?
Jörg W Mittag

@MarkAmery: Hãy nhớ rằng nhiều trong số những "ngôn ngữ phổ biến khác" đó trẻ hơn Ruby hoặc ít nhất là đương đại. Vì vậy, câu hỏi nên là: tại sao những ngôn ngữ khác không theo Ruby (và Smalltalk và Lisp và phần cứng và tài liệu).
Jörg W Mittag

@ JörgWMittag Thú vị - bạn đã truyền cảm hứng cho tôi thực hiện một nghiên cứu lịch sử nhỏ. C ++ có khái niệm "ném" một năm ngoại lệ trước khi Ruby xuất hiện và theo tiếng Anh.stackexchange.com/a/449209/73974 thuật ngữ này thực sự quay trở lại thập niên 70 ... vì vậy tôi nghĩ chúng ta vẫn chỉ trích Ruby sử dụng thuật ngữ được thiết lập và sử dụng nó để có nghĩa là một cái gì đó hoàn toàn khác nhau.
Đánh dấu Amery
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.