זרימת בקרה

catch#

מטפל בחריגה שנזרקה על־ידי בלוק try קודם.

catch הוא החצי השני של מבנה try/catch. הוא אינו הוראה עצמאית: הוא בא אחרי בלוק try, מצהיר על משתנה לקסיקלי VAR שיקבל את ערך החריגה, ומריץ את ה־BLOCK שלו רק כאשר בלוק try מת. אם בלוק try הסתיים כרגיל, בלוק catch מדולג. הבקרה מתחדשת אז בהוראה שאחרי המבנה כולו (או בלוק finally, אם כזה קיים).

יש להפעיל תחביר זה עם use feature 'try'. try ו־catch אינם ניסיוניים החל מ־Perl 5.40.

תקציר#

try BLOCK catch (VAR) BLOCK
try BLOCK catch (VAR) BLOCK finally BLOCK

מה מקבלים בחזרה#

catch אינו קריאת פונקציה ואין לו ערך החזרה במובן של ביטוי. כמו מבני זרימת־בקרה אחרים, ערכו של המבנה try/catch כולו הוא ערך הבלוק שרץ אחרון - שימושי בתוך בלוק do או כביטוי הזנב של תת־שגרה:

my $value = do {
    try {
        fetch_thing(@args);
    }
    catch ($e) {
        warn "fetch failed: $e";
        $DEFAULT;
    }
};

אם try רץ עד תום, המבנה מניב את הערך האחרון של בלוק try ו־VAR לעולם אינו נקשר. אם try מת, המבנה מניב את הערך האחרון של בלוק catch.

הצהרת ה־VAR#

The parentheses after catch are mandatory and must contain exactly one scalar variable declaration. It behaves like a my declaration

  • the my keyword is implicit, as in subroutine signatures:

catch ($e)  { ... }         # $e is a new lexical
catch (my $e) { ... }       # syntax error - 'my' is already implied

ל־VAR יש תיחום לבלוק catch בלבד. הוא אינו נראה בבלוק try הקודם, בכל בלוק finally הבא אחריו, או אחרי המבנה. ערכו הוא מה שבלוק try זרק - מחרוזת, רשימה שעברה המרה למחרוזת לכזו, או הפניה, בדרך כלל עצם חריגה blessed. ראו die לאופן שבו ערך החריגה מיוצר.

בניגוד ל־eval, catch אינו קורא או מנקה את $@; החריגה נוסעת דרך VAR ישירות. $@ אינו נוגע על־ידי המבנה.

מצב גלובלי שהוא נוגע בו#

catch עצמו אינו נוגע במשתנים מיוחדים. מבנה try/catch בכללותו משאיר את $@ ללא שינוי - סטייה מכוונת מ־eval שמטרתה למנוע מקוראים לערבב את שני המטבעות. אם נדרשת בחינה בסגנון $@, יש להשתמש ב־eval במקום.

דוגמאות#

תפיסת חריגת מחרוזת פשוטה ודיווח עליה:

use feature 'try';

try {
    risky();
}
catch ($e) {
    warn "risky failed: $e";
}

בחינה של עצם חריגה מובנה:

try {
    process($record);
}
catch ($e) {
    if (ref($e) && $e->isa('My::NotFound')) {
        return;                       # missing is fine
    }
    die $e;                           # re-throw everything else
}

זריקה מחדש ללא שינוי עם die. מכיוון ש־catch אינו נוגע ב־$@, זורקים מחדש את המשתנה שקושר, לא את $@:

try {
    step();
}
catch ($e) {
    log_error($e);
    die $e;                           # propagates to the next handler
}

ייצור ערך מהמבנה ללא return. שימוש ב־return בתוך try או catch מחזיר מתת־השגרה העוטפת, לא מהמבנה - ראו מקרי קצה:

sub safe_parse {
    my ($text) = @_;
    try {
        parse($text);
    }
    catch ($e) {
        warn "parse error: $e";
        undef;                        # value of the catch block
    }
}

בלוק catch שעצמו מת - החריגה החדשה מופצת החוצה למטפל העוטף הבא (try/catch, eval, או יציאת תוכנית):

try {
    open_db();
}
catch ($e) {
    cleanup();
    die "open_db failed: $e";         # thrown from inside catch
}

מקרי קצה#

  • אינו יכול לעמוד לבדו. catch (VAR) { ... } ללא בלוק try קודם הוא שגיאת תחביר. catch מנותח כחלק ממבנה try, לא כמילת מפתח עצמאית.

  • בדיוק סקלר אחד בסוגריים. הסוגריים הם חובה, חייבים להצהיר על סקלר אחד בדיוק, ומילת המפתח my משתמעת. catch { ... } ללא סוגריים הוא שגיאת תחביר.

  • ל־VAR יש תיחום־בלוק. הוא קיים רק בתוך בלוק catch. בלוק finally שבא אחריו אינו רואה אותו.

  • חריגות שקריות עדיין מפעילות את המטפל. try מבדיל בין ״מת״ לבין ״הסתיים כרגיל״ על־ידי דגל שנקבע בזמן unwind, לא על־ידי אמיתיוּת של ערך החריגה. die 0, die "", ו־die undef כולם נכנסים לבלוק catch; VAR מקבל את הערך כפי שנזרק (עם ההמרה הרגילה של die של undef ל־"Died at ...").

  • $@ אינו נקבע. קוד שמושיט יד אל $@ בתוך בלוק catch קורא את $@ של הקורא, לא את החריגה שזה עתה נתפסה. יש להשתמש ב־VAR.

  • בלוק catch עשוי למות. חריגות שמועלות מתוך catch אינן נתפסות מחדש על־ידי אותו מבנה. הן מופצות למטפל העוטף הבא. זהו המנגנון לזריקה מחדש (die $e) ולדיווח על כשלי ניקוי מתוך המטפל עצמו.

  • return בתוך catch מחזיר מתת־השגרה העוטפת, לא מבלוק catch. הדבר שונה מ־eval BLOCK, שבו return היה יוצא רק מה־eval. כדי להניב ערך מהמבנה, יש להשתמש בביטוי הסופי של הבלוק במקום return.

  • פקדי לולאה (last, next, redo) בתוך catch מכוונים ללולאה עוטפת, לא למבנה. מבנה try/catch אינו עצמו לולאה.

  • caller() אינו רואה את מסגרת ה־catch. מכיוון ש־catch אינו מיירט את return, הוא בלתי נראה ל־caller, כמו while או foreach.

  • משויך רק ל־try שקדם לו מיד. לא ניתן לקבל שני בלוקי catch, ולא למקם הוראות בין try ל־catch. בלוק finally האופציונלי, אם קיים, חייב לבוא אחרי catch.

הבדלים מ־upstream#

תואם מלא ל־upstream Perl 5.42.

ראו גם#

  • try - הבלוק שאת כשלו ה־catch הזה מטפל; תמיד משויך ל־catch אחד בדיוק

  • finally - בלוק שלישי אופציונלי שרץ ללא תלות בשאלה האם בלוק try מת; אינו יכול return או להשתמש בפקדי לולאה

  • die - מייצר את ערך החריגה ש־VAR מקבל; יש להשתמש בו בתוך catch כדי לזרוק מחדש

  • eval - מטבע ישן יותר לטיפול בחריגות שמשתמש ב־$@; יש להושיט יד ל־try/catch במקום בקוד חדש לתיחום ברור יותר ובלי foot-guns של $@

  • $@ - נשאר ללא נגיעה על־ידי try/catch; יש לבחון את VAR במקום