Jste zde

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

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ě.

Přidat komentář