5 ms·
nit: the author describes the '?' operator as both not-magic and magical. IMO an operator that returns is pretty magical. What do others think about '?'? It's
by millstone 8y ago
nit: the author describes the '?' operator as both not-magic and magical.
IMO an operator that returns is pretty magical. What do others think about '?'? It's certainly more ergonomic than pattern matching / monads etc, but it also seems easy to miss if it's buried in the middle of an expression.
I think Swift's try is a bit nicer to use, because it comes at the beginning of the statement, provides more flexibility in how the error should be handled, and supports multiple expressions that may generate errors. On the other hand Swift's error handling is an addition to its type system, instead of being built inside it like Rust.
- james-mcelwain 8y agoI'm unsure what "miss" means here. If you forget to add a ?, it's a compilation error.
- millstone 8y agoWhen I'm reading code, I may not realize that an expression can return, because it can be hard to spot a ? embedded in the middle of the line: sanitize(getFile(getName())?.contents); Swift requires prefixing the line with try, which makes it easier to notice: try sanitize(getFile(getName()).contents) Both are a big improvement over exceptions which have no indication at the call site.
- eridius 8y agoIn Swift you can still bury that `try` in the middle of the expression sanitize(try getFile(getName()).contents)
- kibwen 8y agoBit of a correction, I believe that `try` in Swift must precede an expression that can throw, rather than preceding a statement that can throw, so AFAICT the Swift code would have to be written like this: sanitize((try getFile(getName())).contents) This sort of illustrates why Rust moved away from its own prefix `try!` macro to its postfix `?` operator: in a language that encourages chaining methods, `foo()?.bar()?.qux()?` looks better than `try!(try!(try!(foo()).bar()).qux())`.
- dbaupp 8y agoNo, `try` can be applied to any expression, and will "try" all throwing calls in subexpressions of that: func f() throws -> Int { return 0 } func nothing(_ x: Int) {} func g() throws { nothing(try f()) try (f() + f()) try nothing(f() * f()) } This includes method chains: struct Foo { init() throws {} func bar() throws -> Foo { return self } func qux() throws -> Foo { return self } } try Foo().bar().qux()
- kibwen 8y agoCool, thanks, I looked in the official docs but didn't notice any mention of this feature. :)
- eddyb 8y agoNot sure how many editors' syntax highlighting do this by default, but at least for me, it really helped to make `?` bold red.
- ahartmetz 8y ago"Code is read much more often than written" ;)
- steveklabnik 8y agoI think ? is a perfect example of Stroustrup's Rule: https://thefeedbackloop.xyz/stroustrups-rule-and-layering-over-time/ https://thefeedbackloop.xyz/stroustrups-rule-and-layering-ov... > For new features, people insist on LOUD explicit syntax. > For established features, people want terse notation. I was pretty against ? when it was proposed. I liked try!. I thought ? was hard to read. I thought try! was concise enough. After writing (and reading) lots of code with ?, I'm all in. Heck, ? still isn't extendable for user-defined types, and now I want that! We made ? work in main, and in tests. It's now one of my favorite features of Rust. YMMV.
- moosingin3space 8y agoRust seems to follow Stroustrup's Rule very well as a community. Might explain why I like it so much.
- ahartmetz 8y agoFWIW I don't disagree with Rust's choice, I was just answering the question of parent. I am reading Blandy and Orendorff's Rust book and violently agree with almost all design choices, as an experienced C++ programmer.
- steveklabnik 8y agoNoted :)
- TheCycoONE 8y agoThe gp almost certainly means when reading code, not when writing it. Why did this function return here... I don't see anything. Oh wait, there's a ?. On the other hand error handling can distract from the intention of the code, so in another sense ? makes it easier to read. The author can choose if they prefer ?, try!, or a plain old match or if let / return on the Result.
- nine_k 8y agoIf you see `?` as a monadic bind (some kind of .andThen, see futures / promises), the whole thing reads and makes sense: the rest of the expression is a function call, a lambda that only gets invoked when the `?` passes a value through. So `foo?.bar` is something like match foo { Ok(inside_foo) => Ok(inside_foo.bar), // .bar the lambda. Err(v) => Err(v), } The `do` notation makes it a bit more explicit. It also looks directly lifted from Haskell's approach to work with monads.