TE
TechEcho
Home24h TopNewestBestAskShowJobs
GitHubTwitter
Home

TechEcho

A tech news platform built with Next.js, providing global tech news and discussions.

GitHubTwitter

Home

HomeNewestBestAskShowJobs

Resources

HackerNews APIOriginal HackerNewsNext.js

© 2025 TechEcho. All rights reserved.

Rust should not have provided `unwrap`

54 pointsby goranmoominalmost 3 years ago

32 comments

topspinalmost 3 years ago
Maybe it shouldn&#x27;t have but developing in Rust would be intolerable without .unwrap(). In the real world you wade through API documentation figuring out the types and colors of functions while forever being assaulted by Result&lt;&gt;. Without .unwrap() your progress would be destroyed as you&#x27;d have to immediately deal with every unhappy path. unwrap() allows you nail together the happy path and then revisit the failure modes after you get your intentions working.<p>Also, .unwrap() is fine for unit test mocking you need in real world code.<p>Are irresponsible and&#x2F;or under-resourced programmers abusing .unwrap()? Yes. Obviously. Fortunately, the frictionless .unwrap() makes this blatantly obvious to everyone else (just as profligate use of unsafe {} does,) so detecting crap code is much easier. This is a <i>vast</i> improvement over traditional &#x27;systems&#x27; languages that permit said programmers to ignore failures without so much as a word.<p>The net result is .unwrap() is a win for Rust.
评论 #32111800 未加载
评论 #32111510 未加载
评论 #32112197 未加载
评论 #32111240 未加载
评论 #32117238 未加载
lackeralmost 3 years ago
I feel like the author is only thinking about high-quality production code. Often you&#x27;re just writing a 30-line snippet of throwaway code to test something out. And then unwrap() is a bit nicer than expect(&quot;&quot;).
评论 #32110479 未加载
评论 #32110488 未加载
评论 #32113177 未加载
评论 #32112728 未加载
LegionMammal978almost 3 years ago
What about extremely trivial expected conditions that occur all over the program? &quot;expect()&quot; would get tiring pretty quickly.<p><pre><code> Regex::new(r&quot;\bstruct\b&quot;).unwrap() &#x2F;&#x2F; vs. Regex::new(r&quot;\bstruct\b&quot;).expect(&quot;bad regex string literal&quot;)</code></pre>
评论 #32111021 未加载
评论 #32110726 未加载
评论 #32111026 未加载
评论 #32110752 未加载
评论 #32111083 未加载
dymkalmost 3 years ago
`unwrap` is fine. As the author mentions, it can be converted into `?` without much hassle if the function return type can be changed easily. And `?` looks cleaner than `unwrap`, so the incentives are aligned to nudge developers towards that instead.<p>`unwrap` is plenty explicit, and developers know exactly what it does (and that it might panic) once they&#x27;ve spent a few <i>hours</i> with Rust. It&#x27;s not the subtle footgun the author is making it out to be.
评论 #32110508 未加载
评论 #32111062 未加载
azdavisalmost 3 years ago
You can enforce this with a clippy lint: <a href="https:&#x2F;&#x2F;rust-lang.github.io&#x2F;rust-clippy&#x2F;rust-1.62.0&#x2F;index.html#disallowed_methods" rel="nofollow">https:&#x2F;&#x2F;rust-lang.github.io&#x2F;rust-clippy&#x2F;rust-1.62.0&#x2F;index.ht...</a>
评论 #32120616 未加载
评论 #32110522 未加载
评论 #32110435 未加载
Machaalmost 3 years ago
I tend to agree with preferring alternatives (?, Expect, unwrap_or_else) to unwrap in most cases, but I do still find myself using unwrap in cases where it&#x27;s basically impossible for it to fail. e.g. imagine a parse method that returns a Result in case the input is not a valid whatever, but I&#x27;ve just called it with a literal value.<p>At the same time, looking at one of my codebase, 90% of such cases are in my test code
评论 #32110562 未加载
caylusalmost 3 years ago
&gt; Well, that puts regex compilation in the same category as array indexing in my mind, and means that the default regex compilation function should panic on the user’s behalf<p>I like the Go stdlib idiom for this: many functions have a version with a &quot;Must&quot; prefix that has the behavior of panicking rather than returning an error.<p>e.g. for regexp: <a href="https:&#x2F;&#x2F;pkg.go.dev&#x2F;regexp#MustCompile" rel="nofollow">https:&#x2F;&#x2F;pkg.go.dev&#x2F;regexp#MustCompile</a>
评论 #32111035 未加载
dietricheppalmost 3 years ago
Ugh.<p>The basic argument is something like &quot;all functions should be total&quot;, but the problem is that <i>proving</i> that a function is total is really annoying, so we live with two other consequences instead:<p>1. Some functions are not total, and you just have to know the preconditions.<p>2. Some functions are total, but the type system is not powerful enough to prove this (possibly because they call functions in category #1).<p>The same discussion has been going on for decades in Haskell-land, and there&#x27;s no definitive consensus, but my sense is that we&#x27;d rather tolerate a panic here and there rather than try and deal with the fuss of propagating errors or proving that our functions are total in the first place. For example, take a look at this function. I ask,<p>&gt; Is this a reasonable way to write this?<p><pre><code> struct IVec2(i32, i32); impl IVec2 { fn parse(d: [u8; 8]) -&gt; Self { Vec2(i32::from_le_bytes(d[0..4].try_into().unwrap()), i32::from_le_bytes(d[4..8].try_into().unwrap())) } } </code></pre> Someone from the Rust community will come in and say,<p>&gt; Arguably, unwrap() is unreasonable.<p>...but why? The function is total. It won&#x27;t panic. The response is,<p>&gt; ... an error should be properly handled ...<p>There is no error, the code can&#x27;t fail. I don&#x27;t know of another way to write the function without a panic hidden somewhere, either in an unwrap() or inside an indexing operation. For some reason, some members of the Rust community think of this as a problem to be solved.<p>It&#x27;s not a problem. The unwrap() method is just too damn useful. It&#x27;s too damn useful to be able to write functions that are not total. I&#x27;d say that the key here is coming up with a sensible style for when <i>you</i> are okay with panicking in your code, and then accepting the consequences of that decision.<p>(&quot;Total&quot; means that the function returns a result for all inputs. Unwrap is not total. In Haskell, &quot;head&quot; is not total--it returns the first element of a list, but throws an exception if the list is empty.)
评论 #32110675 未加载
评论 #32110697 未加载
评论 #32110616 未加载
评论 #32110644 未加载
评论 #32110633 未加载
评论 #32110669 未加载
nu11ptralmost 3 years ago
TLDR; - While I agree unwrap&#x2F;expect should not typically be used often in production code there are enough exception cases that Rust would be MUCH worse if they didn&#x27;t exist<p>Longer version:<p>Only skimmed, but I disagree, although I thought the same thing when I started. It sounds wonderful to have panic-free Rust but when when you sit down and think about how that would work you quickly realize it is a trade off between boilerplate and convenience.<p>Rust code can be broken down into mostly three &quot;types&quot;:<p>1. Safe, and can&#x27;t panic (calls nothing that can panic)<p>2. Safe, can panic (.unwrap, .expect, indexing, etc.)<p>3. Unsafe, could do anything<p>The goal when writing Rust IMO is to try and get as much as possible in #1, control #2 carefully by closely held invariants, and avoid #3 as much as possible (in many programs, this turns into no unsafe at all).<p>The problem with avoiding #2 entirely is you will quickly get code that is hard to write, harder to understand, etc. Most often you will see situations like &quot;I just proved this is x in length, so I can always index this with this value&quot;. If you don&#x27;t do this you will have lots of error checking that effectively does nothing. I admit this happens rarely for Option&#x2F;Result and in prod code I rarely use unwrap&#x2F;expect, however, some code would be very tedious to write without unwrap or potentially panicking code, so unwrap&#x2F;expect can be valuable for things like tests, examples, and rapid prototyping.<p>I should probably add that I actually agree with the author that in the _typical_ case, you should not be using unwrap&#x2F;expect, but either pattern matching it or propigating it with ?, but the exceptions to this (testing&#x2F;examples&#x2F;prototyping&#x2F;corner cases) are important enough that I&#x27;m very glad Rust has the &quot;escape hatch&quot; of unwrap&#x2F;expect.
codefloalmost 3 years ago
I agree unwrap is tragically overused in example code, and any non-logic error should use a Result instead — even early in a project. This isn’t a huge overhead. Even in “prototypey” code, it’s not hard to make everything return an anyhow::Result and upgrade to more fine-grained error types later. The main function can return a Result as well (which will be unwrapped by the runtime), making this style almost as concise as the unwrap version.<p>I also agree that panic and crash is usually the correct response to a logic error.<p>However, I think library functions like Regex::new shouldn’t decide for me what I do or don’t consider a logic error — how should the library know where that string comes from? They also shouldn’t be effectively required to offer two overloads for every function just to enable both decisions: there’s already a very clean way to put that decisions into the hands of the library user, Result.unwrap() vs. Result?.<p>Now, about unwrap vs. expect: Since the author and I agree that panics should be reserved for logic errors, the expect() argument is effectively only a debugging aid. Should that be required? I’m torn. Often, which one I personally use reflects my confidence in getting the invariant correct and never hitting the panic in production. That can be risky. A particular team in a particular context might therefore adopt a convention to always use expect instead of unwrap, that’s fine. But I don’t think it’s unreasonable that the language offers a choice here, and in similar places, like the no-parameter variant of panic!().
Cyph0nalmost 3 years ago
It’s still useful in cases where you’ve already handled the error case. For example:<p><pre><code> if res.is_err() { &#x2F;&#x2F; Do something return; } let res = res.unwrap();</code></pre>
评论 #32110467 未加载
评论 #32111432 未加载
wongarsualmost 3 years ago
In the Objections section the author concedes that some API calls most commonly fail on logic errors, like creating a regex, and writing an error message for each of these would be unnecessarily verbose. Their counter to this argument is that there should be two versions of that function, one that panics automatically (and that you&#x27;re going to use 95% of the time) and another that returns a Result (e.g. for user-supplied regex).<p>I get that argument, but imo that would be an even greater mess than having unwrap. It creates a burden for every library author to figure out how to handle errors, and to provide multiple options if necessary. The standard library goes this way sometimes, like with array access (foo[i] panics, foo.get(i) returns result) or println (println! panics, writeln! returns result). But the latter often catches people off guard because they don&#x27;t expect it to panic. Propagating implicit panics to more places in the ecosystem in the name of getting rid of explicit panics with bad error messages (using unwrap) seems like a terrible tradeoff.
smoldesualmost 3 years ago
I feel mixed on this, it&#x27;s kinda like arguing that the Hello World for Java needs to include classes because it starts people off on the right foot.<p>Unwrap is admittedly a stupid tool, and if you&#x27;re starting off in Rust it becomes one of those &quot;when all you have is a hammer, everything looks like a nail&quot; situations. It&#x27;s like a can-opener for fussy functions that want you to write semantic code, and it&#x27;s admittedly quite good at what it does. Too good.<p>Like the author highlights, simply appending &#x27;?&#x27; to the end of your line is a better way to handle 90% of unwrap cases, and the &#x27;panic&#x27; macro should cover most of everything else. Unwrap should really only be used when you disagree with the library developer about error handling. In all other cases, you should probably respect the error propagation of the program. Explaining all that to a new Rust user is hard though, so I don&#x27;t really nitpick with it until people are comfortable with the memory model and control flow of Rust.
stuff4benalmost 3 years ago
[OT] Reading this article makes me want to use Rust so badly. I just don&#x27;t have any places to use it. Everything at work is either Go or Scala or Java. Or front-end shit I&#x27;m not smart enough to do anymore. I want to find a job that&#x27;ll pay me senior engineer rates but let me learn Rust like a baby and eventually do something useful with it...
评论 #32113317 未加载
Arcurualmost 3 years ago
Clippy has a lint that will check for `.unwrap()` calls; <a href="https:&#x2F;&#x2F;rust-lang.github.io&#x2F;rust-clippy&#x2F;master&#x2F;index.html#unwrap_used" rel="nofollow">https:&#x2F;&#x2F;rust-lang.github.io&#x2F;rust-clippy&#x2F;master&#x2F;index.html#un...</a>.<p>It is allowed by default, but you can turn it on in the configuration file.
happytoexplainalmost 3 years ago
I&#x27;m not familiar with Rust, but it sounds like a similar story to `?`, `!`, `rethrows`, and `fatalError` in Swift. I.e.:<p>- Handle nil cases with ? or propagate errors with rethrows.<p>- If you really, seriously can&#x27;t recover, use fatalError with a descriptive message.<p>- ! is strictly for non-production code. It is just a hyper-short way of fatally failing with no message. Yes, maybe you can use it if you <i>just checked for nil</i>, but almost always you can refactor that to omit the !. Because of its convenience, people tend to abuse it.
eminence32almost 3 years ago
I am not convinced that propagation is massively superior to unwrap in example code. Unwraps are nice because they give you error messages with line numbers, but line numbers don&#x27;t seem to come for free with propagation.<p>Consider this code[1]:<p><pre><code> fn main() { let _my_num: u32 = &quot;not a number&quot;.parse().unwrap(); } </code></pre> Running this gives a slightly noisy&#x2F;verbose message, but to my eyes it&#x27;s pretty useful:<p><pre><code> thread &#x27;main&#x27; panicked at &#x27;called `Result::unwrap()` on an `Err` value: ParseIntError { kind: InvalidDigit }&#x27;, src&#x2F;main.rs:2:47 </code></pre> Compare this to similar code with propagation[2]:<p><pre><code> fn main() -&gt; Result&lt;(), Box&lt;dyn std::error::Error&gt;&gt; { let _my_num: u32 = &quot;not a number&quot;.parse()?; Ok(()) } </code></pre> Not only do I find the extra boilerplate distracting for this example code, but if I wanted to run this code in a playground, the error message seems worse as it omits the line number:<p><pre><code> Error: ParseIntError { kind: InvalidDigit } </code></pre> It&#x27;s true that crates like anyhow[3] have robust support for producing backtraces (and I use them all the time in non-example code). But I&#x27;m not inclined to add an external crate to my examples just to avoid calling an unwrap().<p>[1] <a href="https:&#x2F;&#x2F;play.rust-lang.org&#x2F;?version=stable&amp;mode=debug&amp;edition=2021&amp;gist=20e2f21d6a296a09920525e6764837ff" rel="nofollow">https:&#x2F;&#x2F;play.rust-lang.org&#x2F;?version=stable&amp;mode=debug&amp;editio...</a> [2] <a href="https:&#x2F;&#x2F;play.rust-lang.org&#x2F;?version=stable&amp;mode=debug&amp;edition=2021&amp;gist=9a7e8a1a7c0e81de81cc9b7e3c9ce04b" rel="nofollow">https:&#x2F;&#x2F;play.rust-lang.org&#x2F;?version=stable&amp;mode=debug&amp;editio...</a> [3] <a href="https:&#x2F;&#x2F;crates.io&#x2F;crates&#x2F;anyhow" rel="nofollow">https:&#x2F;&#x2F;crates.io&#x2F;crates&#x2F;anyhow</a>
grumpyprolealmost 3 years ago
Unwrap is just the same (or better) than expect(&quot;&quot;). The cat is out the bag with expect. What is needed, is good documentation, style guides, example code etc to guide users towards understanding the trade-offs and making informed decisions. I suppose unwrap could have had a more scary name.
boardwaalkalmost 3 years ago
Yes, we should have unwrap. Rust would move towards insufferable if it did not — sort of like Go and its “you must use every variable” thing.<p>The way forward is a feature allows static checking of whether code paths could possibly panic so people can gradually mark their code as panic-free.
brundolfalmost 3 years ago
I broadly disagree, but others here have covered all the points I would have made around that, so instead I&#x27;ll mention an interesting thought this did bring up for me:<p>&gt; Most of the time I see it, ? would be better and could be used instead with minimal hassle<p>I don&#x27;t agree that you should just bring in a library, as a default strategy, to make it easy to use ? in all cases without thought<p><i>But</i>. For the subset of cases where you could do this without any type changes - where the unwrapped error already has the right type for the containing function - I wonder if it would make sense to have a Clippy rule and&#x2F;or IDE autofix?
GuB-42almost 3 years ago
If a keyword is here, generally, it means there is a use for it. Feel free not to use it, but it is generally a better idea to try to understand why it is there before calling it a mistake.<p>Here, it goes well with what is called &quot;offensive programming&quot;, a take on &quot;defensive programming&quot;. Here the idea is that you crash as soon as something unexpected happen. It is a bug, fix it instead of trying to recover from something you don&#x27;t understand. It is actually common in embedded software, if something is wrong, stop execution and wait for the watchdog to trigger a restart.
cratermoonalmost 3 years ago
See also the discussion for a similar feature in Java, from &quot;Optional: The Mother of all Bikesheds&quot;, where the use and misuse of get() has a central role.<p>video: <a href="https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=Ej0sss6cq14" rel="nofollow">https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=Ej0sss6cq14</a><p>slides: <a href="https:&#x2F;&#x2F;stuartmarks.files.wordpress.com&#x2F;2017&#x2F;03&#x2F;optionalmotherofallbikesheds-devoxxbe2016.pdf" rel="nofollow">https:&#x2F;&#x2F;stuartmarks.files.wordpress.com&#x2F;2017&#x2F;03&#x2F;optionalmoth...</a>
marcosdumayalmost 3 years ago
Ok, that argument is about as old as Rust. With one difference that now the &quot;?&quot; operator exists.<p>So, quick code that doesn&#x27;t care about errors can be done quite simply by just using &quot;?&quot;. The real place where there is a relevant use-case for unwrap is on code examples. And I am really not sure &quot;?&quot; is a good replacement for those, because there is a good chance that you will hit the people trying to learn from your example with a complicated type error before they even get some code to run.
user249almost 3 years ago
When I was learning Rust, unwrap felt like cheating
评论 #32110411 未加载
ianpurtonalmost 3 years ago
I&#x27;d also be happy if it was removed. It&#x27;s too easy to use it rather than going up the learning curve of ?.<p>? Is one of the main benefits of rust.
Animatsalmost 3 years ago
I tend to agree.<p>You can use &quot;unwrap()&quot; on an Option, but you can&#x27;t use &quot;?&quot;. You have to write something like<p><pre><code> let val = v.ok_or(anyhow!(&quot;Error message goes here&quot;))?; </code></pre> which is a bit unwieldy.<p><pre><code> fn square(v: &amp;Option&lt;f32&gt;) -&gt; f32 { Ok(v.unwrap()*v.unwrap()) } </code></pre> becomes<p><pre><code> fn square(v: &amp;Option&lt;f32&gt;) -&gt; Result&lt;f32, Error&gt; { let val = v.ok_or(anyhow!(&quot;None value sent to square&quot;))?; Ok(val*val) } </code></pre> A default message would be useful.<p>Bailing out of the middle of a map expression&#x27;s closure is complicated. In that situation, &quot;?&quot; causes a return of the expression in the closure, not the whole map. There&#x27;s a clever way around this.[1] It supposedly gets optimized so that the iteration quits early, and an array of Result types is not generated and then flattened.<p>You never really <i>need</i> unwrap, but the &quot;right way&quot; can be verbose.<p>[1] <a href="https:&#x2F;&#x2F;stackoverflow.com&#x2F;questions&#x2F;26368288&#x2F;how-do-i-stop-iteration-and-return-an-error-when-iteratormap-returns-a-result" rel="nofollow">https:&#x2F;&#x2F;stackoverflow.com&#x2F;questions&#x2F;26368288&#x2F;how-do-i-stop-i...</a>
评论 #32111571 未加载
评论 #32111300 未加载
评论 #32111114 未加载
al2o3cralmost 3 years ago
I don&#x27;t see how removing `unwrap` like the author dreams of would result in anything other than a lot of `expect(&quot;oh no&quot;)` instead - the problem refactors itself from &quot;sloppy developers use unwrap which has a generic panic message&quot; to &quot;sloppy developers use expect with a generic panic message&quot;.
lordnachoalmost 3 years ago
My only issue with expect is it seems like the wrong name. It sounds like it means &quot;I expect this condition to be true&quot; whereas it really means &quot;if this thing is not ok, print this message and panic&quot;.<p>Why not just call it unwrap_or_errmsg?
pdimitaralmost 3 years ago
As I said in the past, I&#x27;d love a a compiler flag &#x2F; Cargo.toml setting that disables all panicky Rust stdlib API. Obviously many people don&#x27;t need or want that so it should be opt in only for those that do.
assbuttbuttassalmost 3 years ago
Using a mutex basically requires the use of unwrap<p>let lockedValue = mutex.lock().unwrap();<p>The only time .lock() can fail is if another thread panicked and poisoned the mutex, so propagating the panic makes the most sense here.
评论 #32111110 未加载
ghygghualmost 3 years ago
My small gripe with unwrap is that the name is too similar to the perfectly safe unwrap_or (and friends). This is annoying both when reading the code and when grepping.
gwbas1calmost 3 years ago
TLDR: Industrial-strength code should not have &quot;mystery error&quot; failures. Always include some kind of error message; or adopt techniques to abstract away providing the error message.