11

I have this code:

fn do_stuff() -> Result<i32, String> {

    let repo = git2::Repository::open(".")?;
    // ...
}

This doesn't work because git2::Repository::open()'s error type isn't String. (Yes I'm being pretty lazy with my error handling by using Strings. It's a tiny program; sue me.)

error[E0277]: the trait bound `std::string::String: std::convert::From<git2::Error>` is not satisfied
  --> src/main.rs:94:13
   |
94 |    let repo = Repository::open(".")?;
   |               ^^^^^^^^^^^^^^^^^^^^^^ the trait `std::convert::From<git2::Error>` is not implemented for `std::string::String`
   |
   = help: the following implementations were found:
   = help:   <std::string::String as std::convert::From<&'a str>>
   = help:   <std::string::String as std::convert::From<std::borrow::Cow<'a, str>>>
   = note: required by `std::convert::From::from`

I've tried adding this:

impl std::convert::From<git2::Error> for String {
    fn from(err: git2::Error) -> Self {
        err.to_string()
    }
}

But that isn't allowed because it doesn't reference any types defined in this crate.

I know I can probably use .map_err(), but I'd really like it to happen automatically. I kind of feel like I have got this to work before too, which is a bit annoying!

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Timmmm
  • 88,195
  • 71
  • 364
  • 509
  • Yes, you cannot [implement a trait you don't own for a type you don't own](http://stackoverflow.com/q/25413201/155423). – Shepmaster Jan 30 '17 at 16:34

2 Answers2

12

If a type implements std::error::Error, it also implements Display:

pub trait Error: Debug + Display {
    // ...
}

The ToString trait, which provides the method to_string, is implemented for any type that implements Display.

Thus, any type that implements Error can be converted to a String via to_string:

use git2; // 0.13.2

fn do_stuff() -> Result<i32, String> {
    let repo = git2::Repository::open(".").map_err(|e| e.to_string())?;
    unimplemented!()
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • How would you fill the `...` in `map_err(...)` with `to_string()` but without the explicit closure `(|e| ...)`? That is, I'm wondering what it would look like in [this answer](https://stackoverflow.com/a/32857265/545794). – Sean Leather Dec 03 '18 at 12:23
  • 1
    @SeanLeather With Rust 2018 (available at the end of this week in Rust 1.31) you can do `.as_ref().map_err(git2::Error::to_string)` – Shepmaster Dec 03 '18 at 14:37
  • @SeanLeather Do [Rust 2018 edition guide](https://rust-lang-nursery.github.io/edition-guide/); [What are non-lexical lifetimes?](https://stackoverflow.com/q/50251487/155423) help? – Shepmaster Dec 05 '18 at 15:02
  • No, unfortunately. It's not clear to me how non-lexical lifetimes allow the change from `.map_err(|e| e.to_string())` to `.as_ref().map_err(git2::Error::to_string)`. – Sean Leather Dec 07 '18 at 05:45
  • @SeanLeather they don't, it's just to allow one line, otherwise you'd have to break it down into two lines. `as_ref` is the key to make the value in the `Option` a reference. – Shepmaster Dec 07 '18 at 13:12
3

Well it turns out there is a bit in the Rust book about this. It doesn't allow you to convert to String, but apparently all Error types can be converted to Box<Error> so I just replaced String with that. It's cleaner too.

Timmmm
  • 88,195
  • 71
  • 364
  • 509
  • 4
    What's more, `?` and `try!` call `From::from` on their Error argument, meaning that you can use `Box` and `?` to easily coalesce all sorts of errors into generic error, if that's what you want to do. – Josh Lee Jan 30 '17 at 19:00