3 ms·
As 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
by MaulingMonkey 7y ago
As 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.
- nemothekid 7y agoYou don’t need the `map_err` if you are using `?`