Zajímavé chování konstrukce try-catch-finally

Od Petr Zemek, 2009-03-14

U většiny vysokoúrovňových jazyků (především těch, které vychází z objektově orientovaného paradigma) se lze setkat s obsluhou chyb pomocí výjímek a jazykovou konstrukcí try-catch-finally. Jednotlivé jazyky se liší např. absencí klauzule finally (C++) či jiným pojmenováním (try-except-finally v Pythonu a begin-rescue-ensure v Ruby). Ve výsledku ale mají všechny tyto konstrukce podobné chování - pokud dojde v bloku try k vyhození výjimky (ať už přímo, tak i nepřímo při volání nějaké funkce), dojde k vyhledání catch bloku, který danou výjimku odchytí a ošetří či přepošle dál (pokud žádný blok neodpovídá typu vyhozené výjimky, pak se výjimka propaguje dál). Co je ale důležité, je to, že ať už k vyhození výjimky či její obsluze dojde nebo ne, blok finally se vždy vykoná před návratem z bloku, ve které je definován (např. před návratem z funkce), což se v určitých případech hodí (uvolnění zdrojů apod.).

Na první pohled nic zajímavého. Ovšem co myslíte, jakou hodnotu vrátí tato funkce (příklad v jazyce Java)?

public static int f() {
    try {
        return 1;
    } finally {
        return 2;
    }
}

Co bych jako programátor očekával, tak je, že tato funkce bude vracet hodnotu 1, ovšem zde přichází ona vlastnost této konstrukce, která říká, že blok finally se provede vždy před návratem z funkce, takže ve skutečnosti se bude vracet hodnota 2. Ano, odpovídá to vlastnostem konstrukce try-catch-finally, ale podle mého názoru to porušuje princip nejmenšího překvapení. Pokud by došlo v bloku finally k vyvolání výjimky, tak funkce "vrátí" výjimku, což opět nepovažuji za příliš šťastné. (Navíc co když byla ve try bloku vyhozena výjimka - ve finally bloku bude místo ní vyhozena výjimka jiná? Z tohoto důvodu destruktory v C++ nevyhazují výjimky - dobře, mohou výjimku vyhodit, ale C++ rutime v takovém případě zavolá terminate() a jste nahraní.)

Podobné chování lze nalézt i u jiných jazyků, např. funkce f v následujícím příkladu v jazyce Python vrátí také hodnotu 2.

def f():
    try:
        return 1
    finally:
        return 2

Další zajímavé chování ukazuje následující příklad z Javy.

public static void g() {
    while (true) {
        try {
            break;
        } finally {
            continue;
        }
    }
}

Tato funkce způsobí zacyklení programu :). Pokud se ale pokusíme naimplementovat tuto funkci v Pythonu, skončíme s chybou při překladu, protože Python nám nedovolí v bloku finally použít continue...

Ještě bych měl jednu poznámku k jazyku Haskell. Tento jazyk takovouto jazykovou kostrukci neobsahuje, nicméně v modulu IO je funkce bracket, která toto chování emuluje (slouží např. pro automatické uzavírání souborů, pokud dojde při vstupně/výstupní operaci k chybě). Ovšem díky tomu, že se jedná o funkci a ne vestavěný konstrukt jazyka, tak tam podobné problémy jako výše zmíněné odpadají.

Co si o tomto způsobu fungování konstrukce try-catch-finally myslíte vy? Kdybyste navrhovali svůj vlastní jazyk, povolili byste např. return či continue/break či vyhazování výjimek ve finally bloku? Nebo naopak vám toto chování přijde logické a znáte případy, kdy se hodí?

Na závěr jen uvedu, že i to, co bylo vymyšleno s dobrým úmyslem, se dá zneužít a nejinak je tomu i v tomto případě.

Obsah tohoto pole je soukromý a nebude veřejně zobrazen.

Filtrované HTML (využíváno)

  • Povolené HTML značky: <a href hreflang> <em> <strong> <cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <table>
  • Zvýraznění syntaxe kódu lze povolit přes následující značky: <code>, <blockcode>, <bash>, <c>, <cpp>, <haskell>, <html>, <java>, <javascript>, <latex>, <perl>, <php>, <python>, <ruby>, <rust>, <sql>, <text>, <vim>, <xml>, <yaml>.
  • Řádky a odstavce se zalomí automaticky.
  • Webové a e-mailové adresy jsou automaticky převedeny na odkazy.
CAPTCHA
5 + 10 =
Vyřešte tento jednoduchý matematický příklad a vložte výsledek. Např. pro 1+3 vložte 4.
Nějak se mi tady rozmohl spam, takže poprosím o ověření.