Goto in Python ============== I really know that Python was meant to be used without a goto, but sometimes it really makes sense to use it. ## Goto usage patterns: Let's first show a few common and in my opinion sensible (at least sometimes) uses of goto. Also, I'll discuss possible goto-free alternatives of the code and why they feel worse. ### Error handling The most common one, used all over the Linux kernel for example: ```c // ... if(! something_we_dont_want_to_fail()): goto err; // We know it hasn't failed, so we can use it // ... return result; // OK err: clean_up() return 0; // This did not work. ``` Often, there are more goto's for one label, and fall through is utilized, e.g.: ```c // Open the file if (file_empty(f)) goto clean_files; // Read the file if (bad_data(f)) goto clean_files; // Load data into memory if (could_not_process(data)) goto clean_memory; return processed_data; clean_memory: free(data); clean_files: close(f); return 0; ``` The goto-free alternative is to put everything into functions and check all the functions. This might lead to creating functions with no semantics, and that does not generally make the code more readable. The lucky part is, this is the case that is simple to solve with exceptions in much the same way. There is still a case to be made with the multiple cleanup blocks and fall-through, but one of the ways of solving that is to have the exception know what to clean up. That might be a bit ugly, but the goto is way worse. ### Alternative methods of evaluation Sometimes, one would like to try several different paths to get a result, in some order, i.e. with pseudocode like this: ```c int result = 0; // Method 1 (the most likely to work) result = method_1(); if(result) goto done; // Method 1 did not work, try method 2: result = method_2(); if(result) goto done; // Try method 3 maybe? result = method_3(); if (! result) return ERROR; done: // We now have result, so process that // ... ``` Possible options to fixing this: - Create a function that gets the result. In that function, the goto's can be replaced by return's, so that is nice, but the function might not have sensible name and need an unreasonable number of arguments, if the methods are complicated enough. - Reorder the methods to try from the least preffered to most. That obscures the algorithm, and might not be an option, if the methods have side effects. There exists a solution with exceptions: Raise an exception when one method fails, and try other methods in the exception handling block. That leads to complicated catch[^I will call it "catch" block even though in Python this technically is an "except" block, because it seems more obvious what I mean.] blocks, which is not a good practice. And what is worse, for the third method the blocks would be nested! ### Retries Sometimes, you need to try something several times, or skip something. In some code (not originally in C), I had something along the lines of: ```c struct line_with_number get_next_reasonable_line(): // Imagine here a static FILE object, from which I read the lines. int linelen = 255; char *line; static int line_number next_line: getline(line, linelen, file); if (line == NULL) // Just return some kind of error return {.line = NULL, .number = line_number}; line = strip_comments(line); if (strlen(line) == 0) goto next_line; // Look at the line more closely if (we_do_not_like(line)) goto next_line; return {.line = line, .number = line_number}; ``` The "obvious" solution to this is to use `while` and `continue`. But that is not necessarily better -- the code is quite likely to pass only once, so there is no semantics of a cycle. Also, what would be the condition here? We have no data, so maybe `while(true)`. Or in the better case, `do ... while(strlen(line) == 0 || we_do_not_like(line))`. When the condition is not so simple, using infinite loop is the way to go. But such code is not prettier, since it uses unreasonable flow control and loses the jump's meaning. There may be a few more places where a goto is a sensible option, but I don't recall any of them, so they might not be as important. ## What to do in Python? There is no simple way around the second use-case for a goto. In some code, I ended up with following code (and to make it worse, that was inside a function, all of it): ```python # NOTE: This is really ugly goto, simulated using exceptions class EverythingIsOK(Exception): pass try: # Method 1: result = method_1() if result: raise EverythingIsOK() # Goto # Method 1 did not work, try method 2: result = method_2() if result: raise EverythingIsOK() # Try method 3 maybe? result = method_3() if not result: raise OperationalError() # Not OK, Not goto. except EverythingIsOK: # Label pass # Process result ``` This is ugly enough, but can only jump downwards. Jumping upwards probably needs the trick with `continue`, and unfortunately, there is no `do-while` in Python. ```python while True: # Label # code continue # Goto # code break # Boilerplate ``` If you do not like that the goto's look different, you can use this mess: ```python class MyGoto(Exception): pass while True: # Label try: # code raise MyGoto() # Goto # code break except MyGoto: # Even more boilerplate. continue ``` I have no solution for using a single label for jumping from above _and_ below. But I hope I will never see code that needs that. This post is not meant to encourage use of goto. It is considered harmful, and in most cases, rightfully so. But still, there are some reasonable use cases, so it is worth knowing when to use it and what can you do when the language does not support goto. And probably needless to say: I would still rather have native goto support, than need to use awful hacks to simulate the flow which I have in mind.