7 ms·
For rust, it is probably the try (?) operator. Fundamentally, it's just syntax sugar for a match statement with an early return in the Error or None cases, but
by jamincan 4y ago
For rust, it is probably the try (?) operator. Fundamentally, it's just syntax sugar for a match statement with an early return in the Error or None cases, but it really improves the ergonomics of dealing with Result and Option types.
- mncharity 4y agoHere's Rust's try (?) operator.[1] It seems sugar for handling short-circuiting return values. A bit like Swift's try? which converts throws to nil.[2] Less so javascript's short-circuiting optional chainging `?.`[3], which I thought of first. [1] https://doc.rust-lang.org/std/ops/trait.Try.html https://doc.rust-lang.org/std/ops/trait.Try.html [2] https://docs.swift.org/swift-book/ReferenceManual/Expressions.html https://docs.swift.org/swift-book/ReferenceManual/Expression... then /Try Operator/ - section anchors don't look stable. [3] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
- randomdata 4y agoInterestingly, there was once a solid effort to add a try operator to Go. While the proposal was quite well received, upon closer inspection it was realized it would be essentially useless in the real world as, given how the rest of the language works, you almost never would want to simply early return with the value received. The data revealed that the vast majority of the code in the wild that the syntax sugar would replace returned something else. So while it is indeed a nifty feature if the rest of the language is also designed for it, it's not something that is easily tacked on to a language that is not.
- int_19h 4y agoIn Rust, when you use `?`, it includes a step to convert the error type of the expression it was used on into the (possibly different) error type that the current function returns. So if you need to map low-level errors to high-level ones in a consistent way, you'd just do it once when defining that error type.
- randomdata 4y agoWhich too requires the language to have a 'the producer is always right' over a 'the customer is always right' design, which Rust does. It all works well when the language is designed for it, but it has to be there at the macro level. Definitely not a 'microfeature'.
- still_grokking 4y ago> Which too requires the language to have a 'the producer is always right' over a 'the customer is always right' design, which Rust does. What do you mean by those two quoted terms?
- anthlax 4y agoIs there a link to this discussion? This seems interesting. If I can’t make a call to a downstream service, or a file I’m trying to read doesn’t exist, or a s3 bucket 404’s, or almost any other “real world” error I can think of, the only (sane) way I can think of handling this is propagating the error down to the caller (and perhaps logging?) Do you mean to say that it is idiomatic in Go to handle errors by… doing something else? On mobile so I can’t put in a code block, but here’s how I thought Go was written: value, err := some_fn() If err != nil { Return err } (? Operator works here because you could do value := some_fn()? And remove the if statement boilerplate) Do you mean that instead of “return err” Go idiomatically does something else?
- randomdata 4y ago> the only (sane) way I can think of handling this is propagating the error down to the caller A big problem, among many, with doing that is that you leak implementation details out of the abstraction. If, to stick with your example, you have a function that helps you with reading files, the caller shouldn't care where the data is stored. Today it might be the local filesystem, tomorrow S3, and when you make that change nothing about the rest of the program should break. But you can't count on the lower level functions using the same errors. As you suggest, a "a file I’m trying to read doesn’t exist" isn't represented as a "bucket 404", even though at a higher level they are the exact same thing. If you straight returned the "a file I’m trying to read doesn’t exist" error as you got it from the file API, now the caller is going to depend on that, and when you replace it with the S3 function that returns a "bucket 404" error, everything starts to break. What you typically want to do is return a more generalized "not found" error that can remain stable regardless of specific implementation details. There are rare cases where you can get away with simply returning the value up the stack, but in the majority of cases you need to handle the error, either by doing something with it or returning a new error that is more useful to the caller. And, so, try becomes essentially unusable without the language taking a larger macro take on supporting such a feature. Like the sibling comment points out, Rust does "from" conversion when using try (?) to try and avoid encountering the same fate.
- still_grokking 4y ago
- deleted 4y ago[deleted]
- jamincan 4y agoYou have things a bit backwards. The try operator was not conceived until after sum types and traits were already stable parts of the language. The addition was simple, didn't really alter the language in a meaningful way and is almost purely syntactic sugar meant as a quality-of-life improvement for users. (ie. it hits pretty every single point in the article's definition of microfeatures)