3 ms·
> 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 c
by 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...)
- Arnavion 7y ago>`?` uses the `From` trait which means you often don't need `.map_err`. 1. `From` is a global solution to a local problem. All `std::io::Error` must necessarily be converted to the same enum variant regardless of what caused them. Failing to open a file and failing to write to a network socket will create the same variant. 2. `From` does not have access to context anyway. A `From<std::io::Error>` is not going to know that the error was hit specifically when "parsing the /etc/foo.conf file", and a `From<std::string::ParseError>` is not going to know the error was hit when "parsing the bar field of the /etc/foo.conf file because it is set to `baz` which is not an integer". >as long as your error types implement std::error::Error, you shouldn't need an explicit .map_err to box them: This only works when you want your function to return `Box<dyn Error>` itself without any additional context. The conversation was about needing to add context like in the golang example.