5 ms·
"But Rust has no equivalent of handle: the convenience of the ? operator comes with the likely omission of proper handling." what's that supposed to mean? The ?
by 0815test 7y ago
"But Rust has no equivalent of handle: the convenience of the ? operator comes with the likely omission of proper handling." what's that supposed to mean? The ? operator just bails out if an Error result is returned from the called function, and forwards that Error to the caller. Cleanup is performed implicitly by drop implementations (destructors) using the RAII pattern ala C++.
- dralley 7y agoExactly, and the error types have to match, or at least be convertible, or else it won't compile. Seems either off-base or poorly worded.
- MaulingMonkey 7y agoIn the draft design, they give an example of special-case cleanup that would only execute only when an error occurs, not on the success path. You can emulate this with a boolean flag in your RAII types in Rust or C++, that's set or cleared immediately before a successful return, and then doing conditional logic in your Drop/dtor. Or you could do a std::mem::forget before successful returns. But I guess they think this is an important enough case to dedicate syntax to it for ergonomic reasons, which Rust doesn't have - which is what I think they're getting at.
- 0815test 7y ago> execute only when an error occurs You can use the match construct for that, and the Result<T,E> type comes with some utility methods that make it easier to clarify your desired semantics in many cases. The page complains that the "match" syntax is clunky, but I'm not that sure how 'handle' is supposed to be better.
- Arnavion 7y agoThe `handle` is shared by multiple error branches, whereas you would have to write `match` for each one. `Result` combinators like `and_then` only work when the error types are the same.
- MaulingMonkey 7y agoAs Arnavion points out, handle handles multiple error cases. That said, I'm not convinced it's better in practice. For some comparison points - here's how I'd write the Go CopyFile in Rust: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=c833de2b2d697abeed59cd59cec8fd1b https://play.rust-lang.org/?version=stable&mode=debug&editio... Or, if we want to keep a more 1:1 direct mapping to the Go code: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=c833de2b2d697abeed59cd59cec8fd1b https://play.rust-lang.org/?version=stable&mode=debug&editio... Caveats with the "1:1" mapping: 1) In a real rust codebase you'd probably simply forward std::io::Error instead of converting it into a string like I have here, or give it a better error struct/enum type. I've tried to mimic the Go code here, not fully convert to Rust idioms. 2) You could get rid of the .as_ref() spam by just using &str or &Path to be closer to the Go code, but I'd rather stick at least that close to std::fs::copy's file signature. 3) All explicit close operations are dropped as unnecessary vs the Go code. I guess I could've used std::mem::drop to be more explicit? 4) TempPath is obvious overkill for the single remaining error point 5) In temp-file heavy code you'd probably wrap TempPath + File into TempFile. keep could return the interior File as well.
- Arnavion 7y ago>1) In a real rust codebase you'd probably simply forward std::io::Error instead of converting it into a string like I have here, or give it a better error struct/enum type. In all the real code bases I've worked on, there are multiple disparate types of errors that nevertheless have the same context. Example: A function that takes in a path and parses a config file at that path fails if it can't open the file or if the file is malformed. The file can be malformed because indentation is wrong, because there's a string where there should be an integer, or because a required field is missing. All of these are different error types. So a single `std::io::Error` is not possible, and erasing them into a `Box<dyn Error>` or wrapping them in a custom (context-containing) type nevertheless requires writing a `.map_err` per each Result value.
- MaulingMonkey 7y ago`?` uses the `From` trait which means you often don't need `.map_err`. Ignoring crates like error_chain, even the stdlib comes with a From implementation for `Box<dyn Error>` - as long as your error types implement std::error::Error, you shouldn't need an explicit .map_err to box them: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=f8aaf49fee7632058db95e9bc72b31a2 https://play.rust-lang.org/?version=stable&mode=debug&editio... EDIT: That said, adding extra context will often require map_err or similar. But merely type erasing / combining error sources shouldn't need it, unless I'm missing something. (Most of my Rust use so far has been on toy codebases...)
- btown 7y agoIn Rust you could likely make a pair of macros that does this RAII setup. And because you’d explicitly need to import the macro, it’s evident and traceable what’s going on - no Rails-like magic, which the OP rejection is IMO right to avoid. I think Go sometimes swings too far in the simplicity direction, not letting consenting folks choose to use shorthand, but that’s a very valid design decision.
- Arnavion 7y ago`handle` would've allowed arbitrary code to execute in case of an error as opposed to "just" returning it back to the caller. That's what he's talking about. For example, the example returns a custom wrapper around the original error with context that it was a copy operation with such-and-such source and destination. The equivalent in Rust using `failure::Fail::with_context` requires writing the `.with_context(|_| format!("copy: ..."))` on every expression that uses `?` (unless you happen to get lucky and all the inner errors are the same type, so that you can use combinators to combine them into a single `?`-able Result). Edit: And to be clear, this is not limited to `failure::Fail`. Using your own `enum Error { Copy { source: PathBuf, destination: PathBuf, inner: Box<dyn Error> }, ... }` still requires you to write a manual `.map_err(|err| Error::Copy { ... })` after every Result value that you intend to use `?` on.
- nine_k 7y agoIndeed, Go has no RAII.