try#
מריץ בלוק ומנתב כל חריגה שהוא זורק לבלוק catch, עם בלוק finally אופציונלי הרץ תמיד בדרך החוצה.
try/catch הוא תחביר טיפול חריגות מובנה של Perl. בלוק ה־try מבוצע; אם משפט כלשהו בו זורק (דרך die, חריגה לא־נתפסה מקריאה, או שגיאת זמן ריצה), הבקרה עוברת לבלוק ה־catch כשערך החריגה כבול ללקסיקלי בסוגריים. אם שום דבר אינו זורק, בלוק ה־catch מדולג. שלא כמו eval, return בתוך try מחזיר מהתת־שגרה העוטפת, לא מהבלוק - try הוא זרימת בקרה, לא מסגרת קריאה.
התחביר חייב להיות מופעל עם שומר תכונה:
use feature 'try';
תקציר#
try BLOCK catch (VAR) BLOCK
try BLOCK catch (VAR) BLOCK finally BLOCK
מה מקבלים בחזרה#
try הוא משפט, לא ביטוי, אבל הוא מניב את הערך המוערך האחרון של איזה הסתעפות שרצה - בלוק ה־try בהצלחה, או בלוק ה־catch בכשל. בשימוש כמשפט הסופי של תת־שגרה או בלוק do, אותו ערך הופך לתוצאה:
my $value = do {
try {
fetch_thing(@args);
}
catch ($e) {
warn "fetch failed: $e";
$DEFAULT;
}
};
Do not write return inside a try that is being used this way
returnexits the enclosing sub, bypassing the assignment.
הביטוי הסופי של בלוק finally מושלך; הוא אינו יכול לתרום לערך.
מצב גלובלי שהוא נוגע בו#
המשתנה catch#
אחרי catch חייבת לבוא מיד הצהרת משתנה בסוגריים. my מובלע - יש לכתוב catch ($e), לא catch (my $e). המשתנה לקסיקלי לבלוק ה־catch ומחזיק את הערך הנזרק בדיוק כפי ש־die הסר אותו: מחרוזת, הפניה bless, או כל סקלר:
try {
risky();
}
catch ($e) {
if (ref $e && $e->isa('My::Exception')) {
$e->rethrow if $e->is_fatal;
warn $e->message;
}
else {
warn "plain die: $e";
}
}
אין ניתוב catch מרובה לפי מחלקת חריגה - יש לכתוב את הסיווג בעצמכם בתוך בלוק ה־catch היחיד.
finally#
בלוק שלישי אופציונלי רץ על כל מסלול יציאה ממבנה ה־try/catch: ירידה רגילה, חריגה שנתפסה על ידי catch, חריגה שנזרקה מחדש מ־catch, או העברה לא־מקומית (return, last, next, redo, goto) מתוך אחד הבלוקים.
try {
open_resource();
use_resource();
}
catch ($e) {
warn "failed: $e";
}
finally {
close_resource();
}
finally הוא המקום הנכון לקוד שחרור שחייב לרוץ ללא קשר לאיך הקטע המוגן יוצא - מטפלי קובץ, נעילות, שחזור מצב גלובלי.
לבלוק finally יש מגבלות שלשני הבלוקים האחרים אין:
אין
return, איןgoto, איןlast/next/redo. ניסיון להפעיל אחד מאלה היא שגיאת זמן הידור או זמן ריצה.ערך הביטוי הסופי מתעלמים ממנו -
finallyאינו יכול לתרום לערך המונב של המבנה.
finally נשאר ניסיוני ב־Perl 5.42 ומנפיק אזהרה בקטגוריית experimental::try בשימוש. try ו־catch עצמם הוסרו מסטטוס ניסיוני ב־5.40 ואינם מזהירים יותר.
ניגוד ל־eval { ... } / $@#
הדפוס הישן עדיין נתמך, אבל try/catch מתקן שלוש מהמורות ותיקות:
מהמורה |
|
|
|---|---|---|
דריסת | סכנה ממשית, דורש ריקוד | חריגה כבולה ללקסיקלי רענן |
| מחזיר מה־ | מחזיר מהתת־שגרה העוטפת |
הבחנה בין ”אין שגיאה“ ל“השגיאה הייתה שקר“ |
|
|
יש להשתמש ב־try לקוד חדש. יש לפנות ל־eval EXPR רק כשבאמת צריך את ההידור בזמן ריצה של string-eval.
דוגמאות#
לכידת חריגה ורישומה ב־log:
use feature 'try';
try {
my $x = call_a_function();
$x < 100 or die "too big: $x";
send_output($x);
}
catch ($e) {
warn "unable to output a value: $e";
}
print "finished\n";
שימוש ב־try כביטוי בתוך do כדי לבחור fallback:
use feature 'try';
my $config = do {
try { load_config($path) }
catch ($e) { warn "using defaults: $e"; default_config() }
};
שחרור משאב ללא תנאי עם finally:
use feature 'try';
no warnings 'experimental::try'; # finally is still experimental
my $lock = acquire_lock();
try {
do_critical_work();
}
catch ($e) {
warn "critical work failed: $e";
}
finally {
release_lock($lock);
}
סיווג חריגות בתוך בלוק catch יחיד:
use feature 'try';
try {
fetch_remote();
}
catch ($e) {
if (ref $e && $e->isa('Net::Timeout')) {
retry_later();
}
elsif (ref $e && $e->isa('Net::Auth')) {
die $e; # rethrow auth failures
}
else {
warn "unexpected: $e";
}
}
return בתוך try מחזיר מהתת־שגרה העוטפת - לא רק מהבלוק:
use feature 'try';
sub first_valid {
for my $path (@_) {
try {
return load($path); # exits first_valid on success
}
catch ($e) {
warn "skip $path: $e";
}
}
return; # all paths failed
}
מקרי קצה#
שומר תכונה נדרש. ללא
use feature 'try'(או חבילתuse v5.34או מאוחרת יותר המאפשרת אותה),tryהוא מזהה רגיל והמבנה הוא שגיאת תחביר.catchהוא חובה.try { ... }לבדו אינו חוקי - לכלtryנדרשcatch. יש להשתמש ב־deferאוfinallyאם רוצים ניקוי ללא טיפול חריגות, מזווג עםcatch ($e) { die $e }טריוויאלי לזריקה מחדש אם גם רוצים לאפשר לחריגה להתפשט.זריקה מחדש. בתוך
catch,die $eזורק מחדש. אין זריקה מחדש מובלעת אם פשוט נופלים מסוף ה־catch; עשייה כך בולעת את החריגה. יש להחליט בכוונה.$@אינו מנוקה על ידי כניסה ל־tryואינו מוגדר על ידי חריגה שנתפסה. אסור לקרוא את$@בתוךcatchבציפייה לערך הנזרק - יש לקרוא את הלקסיקלי.caller()אינו רואה אתtry. כמוwhileאוforeach,tryאינו מסגרת קריאה.callerמדלג עליו, וזה נכון: מכיוון ש־returnמתפשט דרךtry, ל־tryאין סמנטיקת החזרה עצמאית שכדאי לחשוף לאינטרוספקציה.בקרות לולאה עוברות דרכם.
last,next, ו־redoבתוךtryאוcatchפועלים על הלולאה העוטפת, לא על מבנה ה־try. בלוקfinallyעדיין ירוץ לפני שההעברה מסתיימת.מגבלות
finallyנאכפות.return,goto, ובקרות לולאה בתוךfinallyהן שגיאות. אם צריך ניקוי מותנה שעלול לבטל, יש לעשות זאת ב־catchאו מחוץ למבנה.אין סוכר ביטוי־
tryמעבר להנבת משפט אחרון.tryאינו ביטוי כללי; הוא יכול לייצר ערך רק כשהוא המשפט הסופי של בלוק המניב (do, תת־שגרה).my $x = try { ... } catch ($e) { ... };היא שגיאת תחביר.
הבדלים מהמקור#
תואם במלואו ל־Perl 5.42 במקור.
tryו־catchאינם ניסיוניים (החל מ־5.40) ואינם מנפיקים אזהרות.finallyעדיין ניסיוני ב־5.42 ומנפיק אזהרותexperimental::tryבשימוש; יש להשתיק עםno warnings 'experimental::try'.
ראו גם#
catch- הבלוק השני שהוא חובה; מקבל את החריגה בלקסיקלי בסוגרייםfinally- בלוק שלישי אופציונלי לניקוי ללא תנאי; עדיין ניסיוני ב־5.42die- צד הזריקה של המבנה; כל סקלר (מחרוזת, הפניה bless, עצם) יכול להיות הערך הנזרקeval- הדפוס הישן של block-eval /$@, עדיין נדרש עבורevalשל מחרוזת; עדיףtryלטיפול חריגות טהורdefer- ניקוי ללא תנאי ללאtry/catchסובב; הבסיס הסמנטי שעליוfinallyמוגדרreturn- בתוךtryמחזיר מהתת־שגרה העוטפת, לא מהבלוק; בתוךfinallyזוהי שגיאה