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