{"id":4515,"date":"2018-12-24T16:33:12","date_gmt":"2018-12-24T22:33:12","guid":{"rendered":"http:\/\/neosmart.net\/blog\/?p=4515"},"modified":"2018-12-24T18:00:25","modified_gmt":"2018-12-25T00:00:25","slug":"rust-ignore-result","status":"publish","type":"post","link":"https:\/\/neosmart.net\/blog\/rust-ignore-result\/","title":{"rendered":"ignore-result: A simple crate to make ignoring rust Results safe and easy"},"content":{"rendered":"<p><a href=\"https:\/\/neosmart.net\/blog\/wp-content\/uploads\/rust-logo.png\" rel=\"follow\"><img loading=\"lazy\" decoding=\"async\" class=\"alignright size-thumbnail wp-image-4454 colorbox-4515\" src=\"https:\/\/neosmart.net\/blog\/wp-content\/uploads\/rust-logo-150x150.png\" alt=\"\" width=\"150\" height=\"150\" srcset=\"https:\/\/neosmart.net\/blog\/wp-content\/uploads\/rust-logo-150x150.png 150w, https:\/\/neosmart.net\/blog\/wp-content\/uploads\/rust-logo-600x600.png 600w, https:\/\/neosmart.net\/blog\/wp-content\/uploads\/rust-logo-300x300.png 300w, https:\/\/neosmart.net\/blog\/wp-content\/uploads\/rust-logo.png 1024w\" sizes=\"auto, (max-width: 150px) 100vw, 150px\" \/><\/a>Rust, both by design and by convention, has a fairly strongly defined model for strict error handling, designed to force developers to deal with errors up front rather than assume they don&#8217;t exist. If you stick to a few conventions and principles for best practice, error handling becomes fairly straight-forward (although what you ultimately do with the errors is a different question) if you are living in an all-rust world. The problems start when you step foot outside of the comfortable world of crates and <code>Result<\/code>s, such as when dealing with FFI to interface with C libraries or using rust in an embedded context.<\/p>\n<p>The typical approach for dealing with errors in rust in 2018 is to have any function that can encounter a scenario wherein it is unable to return a valid value declare a return type of <code>Result&lt;T, E&gt;<\/code>\u00a0where <code>T<\/code>\u00a0is the expected result type in normal cases and <code>E<\/code>\u00a0is a type covering possible errors that can arise during execution. When calling from one such function into other functions with non-guaranteed success, the typical control flow in the event of an error is almost always &#8220;early out&#8221;:<\/p>\n<p><!--more--><\/p>\n<pre><code class=\"language-rust\">\/\/\/ A function that does something and produces no result,\r\n\/\/\/ except in case of an error, in which case it returns an\r\n\/\/\/ instance of the type `Error`\r\nfn do_something() -&gt; Result&lt;(), Error&gt; {\r\n    inner_func_call();\r\n    inner_func_call_may_fail()?;\r\n    return Ok(());\r\n}\r\n<\/code><\/pre>\n<p>In the example above, the <code>?<\/code>\u00a0in <code>inner_func_call_may_fail()?<\/code>\u00a0is shorthand for &#8220;if the result of <code>inner_func_may_fail()<\/code>\u00a0is an error of type <code>Error<\/code>, bubble it up to the caller immediately rather than continuing on to the next line in this function (and if it produced a result, &#8220;unwrap&#8221; the <code>Ok(whatever)<\/code>\u00a0result to evaluate to <code>whatever<\/code>\u00a0directly).<\/p>\n<p>So far, so good. I&#8217;m not going to delve into the nitty-gritty of <code>match<\/code>\u00a0blocks, what an <code>Error<\/code>\u00a0type declaration looks like, or what you ultimately do with the error when it can bubble up no more. But let&#8217;s look at a common practice from the C world:<\/p>\n<pre><code class=\"language-c\">int destroy_resource(Foo *foo) {\r\n    if (_lucky) {\r\n        return STATUS_SUCCESS;\r\n    }\r\n    return STATUS_SOME_ERROR;\r\n}\r\n\r\nint do_something() {\r\n    Foo *foo = create_resource();\r\n    if (!foo) {\r\n        return STATUS_CREATION_FAILURE;\r\n    }\r\n    int result = use_foo(foo);\r\n    if (result != STATUS_SUCCESS) {\r\n        destroy_resource(foo);\r\n        \/\/ bubble up the original `result`\r\n        return result;\r\n    }\r\n\r\n    \/\/ do some other stuff\r\n    result = destroy_resource(foo);\r\n    if (result != STATUS_SUCCESS) {\r\n        return result;\r\n    }\r\n    return STATUS_SUCCESS;\r\n}\r\n<\/code><\/pre>\n<p>Pay close attention to what happens in case <code>do_something()<\/code>\u00a0failed to execute <code>use_foo()<\/code>\u00a0successfully: it runs a cleanup function\u00a0<em>that may fail<\/em> but bubbles up the original error <code>result<\/code>\u00a0rather than the error returned by <code>destroy_resource()<\/code>\u00a0&#8211; which makes sense, since it&#8217;s the original error that started this problem in the first place. In fact, in this particular case we don&#8217;t even handle the result of <code>destroy_resource()<\/code>\u00a0at all, mainly because there&#8217;s nothing we can do about it. It was a &#8220;best effort&#8221; attempt at freeing allocated resources &#8211; we called the function for its side effects, but we don&#8217;t\u00a0<em>rely<\/em> on the result of the function for anything, i.e. we are not predicating future actions based on assumption that this call succeeded.<\/p>\n<p>In rust, in developing an abstraction for this C API (so you still need to worry about things like resource allocation and memory leaks since they haven&#8217;t yet been abstracted away), you might be tempted to write it as follows:<\/p>\n<pre><code class=\"language-rust\">fn do_something() -&gt; Result&lt;(), E&gt; {\r\n    let foo = create_foo()?;\r\n    let result = use_foo(&foo);\r\n    if result.is_err() {\r\n        free_foo(foo)?;\r\n        return result;\r\n    }\r\n    \/\/ do other stuff\r\n    free_foo(foo)?;\r\n    return Ok(());\r\n}\r\n<\/code><\/pre>\n<p>Or if you&#8217;re a masochist that insists on using <code>match<\/code> exclusively and shuns <code>return ...;<\/code> at the end of function blocks &#8220;because clippy said so&#8221;, you might write it like this instead:<\/p>\n<pre><code class=\"language-rust\">fn do_something() -&gt; Result&lt;(), Error&gt; {\r\n    let foo = create_foo()?;\r\n    match use_foo(&amp;foo) {\r\n        Ok(_) =&gt; {},\r\n        Err(e) =&gt; {\r\n            free_foo(foo)?;\r\n            return Err(e);\r\n        }\r\n    }\r\n    \/\/ do other stuff\r\n    free_foo(foo)?;\r\n    Ok(())\r\n}\r\n<\/code><\/pre>\n<p>The point is, the error handling above in both cases is likely wrong, because the error that gets bubbled up here is the result of <code>free_foo()<\/code>, which went wrong only after <code>use_foo()<\/code> failed. The <code>result<\/code>\u00a0of <code>use_foo()<\/code>\u00a0contains the real <code>Err(e: Error)<\/code>\u00a0we are interested in, that is what should have been bubbled up to the caller instead. If you were to instead omit the <code>?<\/code>\u00a0from that call to <code>free_foo()<\/code>, the correct error (<code>result<\/code>) would be bubbled up to the caller &#8212; but the compiler will complain about an used result:<\/p>\n<blockquote><p>warning: unused result which must be used<\/p><\/blockquote>\n<p>A warning that comes from a good place, but is nonetheless complaining about a purposely ignored return value (there&#8217;s nothing we can do about it here\u00a0<em>and<\/em> there&#8217;s a more correct <code>Error<\/code>\u00a0to be bubbled up).<\/p>\n<p>Since there&#8217;s literally nothing we can do about an inability to free the <code>Foo<\/code>\u00a0instance, if we\u00a0<em>were<\/em> to handle the result to try and silence the warning, the code would necessarily look something like this:<\/p>\n<pre><code class=\"language-rust\">fn do_something() -&gt; Result&lt;(), Error&gt; {\r\n    let foo = create_foo()?;\r\n    let result = use_foo(&amp;foo);\r\n    if result.is_err() {\r\n        match free_foo(foo) {\r\n            _ =&gt; {}\r\n        };\r\n        return result;\r\n    }\r\n    \/\/ do other stuff\r\n    free_foo(foo)?;\r\n    return Ok(());\r\n}\r\n<\/code><\/pre>\n<p>With an empty <code>match<\/code> block that serves no purpose but to silence the compiler (and gets optimized away into nothing). A beginner to rust might be tempted to use <code>free_foo(foo).unwrap()<\/code>\u00a0instead to work around that warning, but that would be a\u00a0<em>grave<\/em> mistake since the function might very well fail, we just don&#8217;t care if it does &#8212; but calling <code>.unwrap()<\/code>\u00a0on a <code>Result<\/code>\u00a0means incurring a panic if it indeed evaluated to an error.<\/p>\n<p><code>ignore-result<\/code>\u00a0is a very simple (<code>no_std<\/code>) crate that extends <code>Result&lt;_, _&gt;<\/code>\u00a0with a <code>.ignore() -&gt; ()<\/code>\u00a0method that consumes a <code>Result&lt;T,E&gt;<\/code>\u00a0coming out of a function and always returns <code>()<\/code>\u00a0&#8211; regardless of what the <code>T<\/code>\u00a0and <code>E<\/code>\u00a0types were. This represents a deliberate handling of both the <code>Ok<\/code>\u00a0and <code>Err<\/code>\u00a0variants on your end (quieting the warnings), discards an error we can&#8217;t do anything about\u00a0<em>while simultaneously blocking us from assuming success<\/em> by return <code>()<\/code>\u00a0regardless of what <code>T<\/code>\u00a0was in this case. In calling <code>.ignore()<\/code>, you are explicitly saying &#8220;I don&#8217;t care of this function succeeds or fails, but I need to call it anyway (and I&#8217;ll pray it succeeds but no sweat off my back if it doesn&#8217;t),&#8221; which is perfectly safe since you can&#8217;t violate that contract with the code\/compiler by then going ahead and using the <code>Ok(whatever)<\/code>\u00a0variant (like a call to <code>.unwrap()<\/code>\u00a0would return).<\/p>\n<p>Compare the resulting code to the variants above, and I think you&#8217;ll agree it&#8217;s simpler, shorter, more expressive, and nicer to boot:<\/p>\n<pre><code class=\"language-rust\">fn do_something() -> Result<(), E> {\r\n    let foo = create_foo()?;\r\n    let result = use_foo(&foo);\r\n    if result.is_err() {\r\n        free_foo(foo).ignore();\r\n        return result;\r\n    }\r\n    \/\/ do other stuff\r\n    free_foo(foo)?;\r\n    return Ok(());\r\n}\r\n<\/code><\/pre>\n<p>I don&#8217;t expect that typical rust developers coding within the rust ecosystem will have need of this crate, it&#8217;s expressly for use by developers interacting with non-rusty APIs. It doesn&#8217;t do anything magic or anything special, but it does make your code (and the intend behind it) much clearer and more succinct than an empty <code>match<\/code>\u00a0block ever could. The crate is <code>no_std<\/code>\u00a0with zero dependencies (the <code>IgnoreResult<\/code>\u00a0trait was yanked from some embedded rust code for a rust-powered RFM69HCW library) and ideally compiles away to nothing at all.<\/p>\n<p>As the <code>Ignore<\/code>\u00a0name is already in use by another (wonderful) library, this crate <a href=\"https:\/\/crates.io\/crates\/ignore-result\" rel=\"nofollow\">can be found at crates.io<\/a> under the name <code>ignore-result<\/code>\u00a0and is released as an open source (MIT-licensed) library <a href=\"https:\/\/github.com\/neosmart\/ignore-result\" rel=\"nofollow\">to our github profile<\/a>. The docs <a href=\"https:\/\/docs.rs\/ignore-result\/\" rel=\"nofollow\">are available on docs.rs<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Rust, both by design and by convention, has a fairly strongly defined model for strict error handling, designed to force developers to deal with errors up front rather than assume they don&#8217;t exist. If you stick to a few conventions &hellip; <a href=\"https:\/\/neosmart.net\/blog\/rust-ignore-result\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":505,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[1],"tags":[989,994,52,936],"class_list":["post-4515","post","type-post","status-publish","format-standard","hentry","category-software","tag-crate","tag-ignore-result","tag-open-source","tag-rust"],"aioseo_notices":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p4xDa-1aP","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/neosmart.net\/blog\/wp-json\/wp\/v2\/posts\/4515","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/neosmart.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/neosmart.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/neosmart.net\/blog\/wp-json\/wp\/v2\/users\/505"}],"replies":[{"embeddable":true,"href":"https:\/\/neosmart.net\/blog\/wp-json\/wp\/v2\/comments?post=4515"}],"version-history":[{"count":7,"href":"https:\/\/neosmart.net\/blog\/wp-json\/wp\/v2\/posts\/4515\/revisions"}],"predecessor-version":[{"id":4522,"href":"https:\/\/neosmart.net\/blog\/wp-json\/wp\/v2\/posts\/4515\/revisions\/4522"}],"wp:attachment":[{"href":"https:\/\/neosmart.net\/blog\/wp-json\/wp\/v2\/media?parent=4515"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/neosmart.net\/blog\/wp-json\/wp\/v2\/categories?post=4515"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/neosmart.net\/blog\/wp-json\/wp\/v2\/tags?post=4515"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}