Comparing error handling in Java, Go and Rust.

Error checking is everywhere. Sometimes it’s more complex than other time. Take file managing for example. You have to remember to close the file every time you return… If it’s not automatic.

Let’s see how Java does it. First, we’ll look at Java 6-.

<span>BufferedReader</span> <span>reader</span><span>;</span>
<span>try</span> <span>{</span>
<span>reader</span> <span>=</span> <span>new</span> <span>BufferedReader</span><span>(</span><span>new</span> <span>FileInputStream</span><span>(</span><span>"test.txt"</span><span>));</span>
<span>// read file</span>
<span>if</span> <span>(</span><span>condition</span><span>)</span> <span>{</span>
<span>// closed by finally block</span>
<span>return</span><span>;</span>
<span>}</span>
<span>// closed by finally block</span>
<span>}</span> <span>catch</span><span>(</span><span>Exception</span> <span>e</span><span>)</span> <span>{</span>
<span>// do proper error handling</span>
<span>// closed by finally block</span>
<span>}</span> <span>finally</span> <span>{</span>
<span>try</span> <span>{</span>
<span>reader</span><span>.</span><span>close</span><span>();</span>
<span>}</span> <span>catch</span><span>(</span><span>Exception</span> <span>e</span><span>)</span> <span>{</span>
<span>// do proper error handling</span>
<span>}</span>
<span>}</span>
<span>System</span><span>.</span><span>out</span><span>.</span><span>println</span><span>(</span><span>"File read"</span><span>);</span>
<span>BufferedReader</span> <span>reader</span><span>;</span>
<span>try</span> <span>{</span>
    <span>reader</span> <span>=</span> <span>new</span> <span>BufferedReader</span><span>(</span><span>new</span> <span>FileInputStream</span><span>(</span><span>"test.txt"</span><span>));</span>
    <span>// read file</span>
    <span>if</span> <span>(</span><span>condition</span><span>)</span> <span>{</span>
        <span>// closed by finally block</span>
        <span>return</span><span>;</span>
    <span>}</span>

    <span>// closed by finally block</span>
<span>}</span> <span>catch</span><span>(</span><span>Exception</span> <span>e</span><span>)</span> <span>{</span>
    <span>// do proper error handling</span>
    <span>// closed by finally block</span>
<span>}</span> <span>finally</span> <span>{</span>
    <span>try</span> <span>{</span>
        <span>reader</span><span>.</span><span>close</span><span>();</span>
    <span>}</span> <span>catch</span><span>(</span><span>Exception</span> <span>e</span><span>)</span> <span>{</span>
        <span>// do proper error handling</span>
    <span>}</span>
<span>}</span>

<span>System</span><span>.</span><span>out</span><span>.</span><span>println</span><span>(</span><span>"File read"</span><span>);</span>
BufferedReader reader; try { reader = new BufferedReader(new FileInputStream("test.txt")); // read file if (condition) { // closed by finally block return; } // closed by finally block } catch(Exception e) { // do proper error handling // closed by finally block } finally { try { reader.close(); } catch(Exception e) { // do proper error handling } } System.out.println("File read");

Enter fullscreen mode Exit fullscreen mode

Oh my God. OK, that is HORRIBLE. Let’s see how Java 7+ does.

<span>try</span><span>(</span><span>BufferedReader</span> <span>reader</span> <span>=</span> <span>new</span> <span>BufferedReader</span><span>(</span><span>new</span> <span>FileInputStream</span><span>(</span><span>"test.txt"</span><span>)))</span> <span>{</span>
<span>// read file</span>
<span>if</span> <span>(</span><span>condition</span><span>)</span> <span>{</span>
<span>// automatically closed</span>
<span>return</span><span>;</span>
<span>}</span>
<span>// automatically closed</span>
<span>}</span> <span>catch</span><span>(</span><span>Exception</span> <span>e</span><span>)</span> <span>{</span>
<span>// do proper error handling</span>
<span>// automatically closed</span>
<span>}</span>
<span>System</span><span>.</span><span>out</span><span>.</span><span>println</span><span>(</span><span>"File read"</span><span>)</span>
<span>try</span><span>(</span><span>BufferedReader</span> <span>reader</span> <span>=</span> <span>new</span> <span>BufferedReader</span><span>(</span><span>new</span> <span>FileInputStream</span><span>(</span><span>"test.txt"</span><span>)))</span> <span>{</span>
    <span>// read file</span>
    <span>if</span> <span>(</span><span>condition</span><span>)</span> <span>{</span>
        <span>// automatically closed</span>
        <span>return</span><span>;</span>
    <span>}</span>

    <span>// automatically closed</span>
<span>}</span> <span>catch</span><span>(</span><span>Exception</span> <span>e</span><span>)</span> <span>{</span>
    <span>// do proper error handling</span>
    <span>// automatically closed</span>
<span>}</span>

<span>System</span><span>.</span><span>out</span><span>.</span><span>println</span><span>(</span><span>"File read"</span><span>)</span>
try(BufferedReader reader = new BufferedReader(new FileInputStream("test.txt"))) { // read file if (condition) { // automatically closed return; } // automatically closed } catch(Exception e) { // do proper error handling // automatically closed } System.out.println("File read")

Enter fullscreen mode Exit fullscreen mode

That is… very good. Congrats, Java. Let’s see how Go does it.

<span>file</span><span>,</span> <span>err</span> <span>:=</span> <span>os</span><span>.</span><span>Open</span><span>(</span><span>"test.txt"</span><span>)</span> <span>// no semicolon :(</span>
<span>// i love semicolons :(</span>
<span>if</span> <span>err</span> <span>!=</span> <span>nil</span> <span>{</span>
<span>// do proper error handling</span>
<span>}</span>
<span>defer</span> <span>file</span><span>.</span><span>Close</span><span>()</span>
<span>// read file</span>
<span>if</span> <span>condition</span> <span>{</span>
<span>// closed by defer</span>
<span>return</span>
<span>}</span>
<span>fmt</span><span>.</span><span>Println</span><span>(</span><span>"File read"</span><span>)</span>
<span>// File closed on return, so you probably wanna return ASAP</span>
<span>file</span><span>,</span> <span>err</span> <span>:=</span> <span>os</span><span>.</span><span>Open</span><span>(</span><span>"test.txt"</span><span>)</span> <span>// no semicolon :(</span>
                                 <span>// i love semicolons :(</span>
<span>if</span> <span>err</span> <span>!=</span> <span>nil</span> <span>{</span>
    <span>// do proper error handling</span>
<span>}</span>
<span>defer</span> <span>file</span><span>.</span><span>Close</span><span>()</span>
<span>// read file</span>
<span>if</span> <span>condition</span> <span>{</span>
    <span>// closed by defer</span>
    <span>return</span>
<span>}</span>

<span>fmt</span><span>.</span><span>Println</span><span>(</span><span>"File read"</span><span>)</span>
<span>// File closed on return, so you probably wanna return ASAP</span>
file, err := os.Open("test.txt") // no semicolon :( // i love semicolons :( if err != nil { // do proper error handling } defer file.Close() // read file if condition { // closed by defer return } fmt.Println("File read") // File closed on return, so you probably wanna return ASAP

Enter fullscreen mode Exit fullscreen mode

One thing to note with Go: Independent error handling. You can’t handle all errors clumped together. This is GOOD. This let’s you specify where exactly it went wrong. Errors in Go are also pretty much strings. You make a custom error with errors.New(“oh no”). Good stuff
Now let’s take a look at Rust.

<span>{</span> <span>// Start a new scope. You don't *have* to do this</span>
<span>let</span> <span>file</span> <span>=</span> <span>match</span> <span>File</span><span>::</span><span>open</span><span>(</span><span>"test.txt"</span><span>)</span> <span>{</span>
<span>Ok</span><span>(</span><span>file</span><span>)</span> <span>=></span> <span>file</span><span>,</span>
<span>Err</span><span>(</span><span>err</span><span>)</span> <span>=></span> <span>{</span>
<span>// do proper error handling</span>
<span>return</span><span>;</span>
<span>}</span>
<span>};</span>
<span>// read file</span>
<span>if</span> <span>condition</span> <span>{</span>
<span>// out of scope - automatically closed</span>
<span>return</span><span>;</span>
<span>}</span>
<span>// out of scope - automatically closed</span>
<span>}</span>
<span>println!</span><span>(</span><span>"File read"</span><span>);</span>
<span>{</span> <span>// Start a new scope. You don't *have* to do this</span>
    <span>let</span> <span>file</span> <span>=</span> <span>match</span> <span>File</span><span>::</span><span>open</span><span>(</span><span>"test.txt"</span><span>)</span> <span>{</span>
        <span>Ok</span><span>(</span><span>file</span><span>)</span> <span>=></span> <span>file</span><span>,</span>
        <span>Err</span><span>(</span><span>err</span><span>)</span> <span>=></span> <span>{</span>
            <span>// do proper error handling</span>
            <span>return</span><span>;</span>
        <span>}</span>
    <span>};</span>
    <span>// read file</span>
    <span>if</span> <span>condition</span> <span>{</span>
        <span>// out of scope - automatically closed</span>
        <span>return</span><span>;</span>
    <span>}</span>

    <span>// out of scope - automatically closed</span>
<span>}</span>

<span>println!</span><span>(</span><span>"File read"</span><span>);</span>
{ // Start a new scope. You don't *have* to do this let file = match File::open("test.txt") { Ok(file) => file, Err(err) => { // do proper error handling return; } }; // read file if condition { // out of scope - automatically closed return; } // out of scope - automatically closed } println!("File read");

Enter fullscreen mode Exit fullscreen mode

After writing this, I had to look at the code again, thinking “that’s it”.
Like you can see, I clearly like Rust’s the most. Java 7’s is also good. Go’s is good, but defer statements only execute on function end, not scope based. You can of course close it manually, but that sucks too.
On the other hand, Go error types are amazing to work with. errors.New("custom error") is amazing. Java’s is not too bad, but Rust’s is “worst” for us lazy people, but also the most powerful. Plus there are macros that fix this.

原文链接:Comparing error handling in Java, Go and Rust.

© 版权声明
THE END
喜欢就支持一下吧
点赞15 分享
If you don’t try, you’ll never know. So try.
如果你不去试,你永远也不知道结果,所以去试试吧
评论 抢沙发

请登录后发表评论

    暂无评论内容