Converting external to own Result/Error type


#1

I’m having trouble with the error the impl does not reference any types defined in this crate; only traits defined in the current crate can be implemented for arbitrary types [E0117]. My intent is to simply convert an external error type to my own error type, which should simply be a string.

extern crate sqlite3;

use std::borrow::Cow;

use self::sqlite3::SqliteError;

type MyError = String; // or Cow<'a, str>

impl Into<MyError> for SqliteError
{
    fn into(self) -> MyError
    {
        self.desc
    }
}

Obviously MyError is a type in this crate, so I don’t get what the issue is. The documentation on error handling also doesn’t give any hints on such conversions, which I think are necessary for many projects (and I’ll improve the docs when this is solved). Also tried From with no success.


#2

Since I just found the answer myself, I’ll just put this online for others to hopefully find it useful.

The problem is that I’m using String as own error type, which is not in my crate. The type XXX = String only creates a type alias, without creating a new type in my create. Therefore, I have to define my own error type using struct:

extern crate sqlite3;

use std::borrow::Cow;

use self::sqlite3::{SqliteError, SqliteErrorCode};

pub struct MyError<'a> { msg: Cow<'a, str> }

impl<'a> Into<MyError<'a>> for SqliteError
{
    fn into(self) -> MyError<'a>
    {
        MyError { msg: self.desc.into() }
    }
}

fn main () {
    let sqlite_error = SqliteError { detail: None, kind: SqliteErrorCode::SQLITE_MISUSE, desc: "something failed" };
    let err : MyError = sqlite_error.into();
    println!("Error: {}", err.msg);
}

#3

Good deduction! Type aliases can be a little tricky, I think I tried the same thing when I was learning Rust and got just as frustrated when I learned it wouldn’t work the way I expected. This should work pretty well.

However, it looks like your error type might not be the most informative. Since SqliteError::desc is an &'static str, a constant string, it can’t have any additional details that might be pertinent to the specific error returned. The fmt::Display impl for SqliteError actually appears to prefer self.detail when it is available.

Also, MyError doesn’t need to use Cow<'a, str> because there’s (currently) no useful shorter lifetime to bind it to (like if you had a non-'static string to pass instead). Cow<'static, str> should suffice, and then you can also use it to store a String if you want to use SqliteError::detail instead.

Finally, it would be more idiomatic to impl From<SqliteError> for MyError> so that the type you’re implementing it for is in your crate. This has the additional benefit of activating the following blanket impl: impl<T, U> Into<U> for T where U: From<T>. Basically, by implementing From<SqliteError> for MyError you get Into<MyError> for SqliteError for free. Unfortunately, there can’t be one for the vice-versa case because that would create a circular dependency.

The try!() macro uses From as well so without an impl From<SqliteError> for MyError you couldn’t use the try!() macro to automatically convert from the former to the latter. So there’s another win!

pub struct MyError { msg: Cow<'static, str> }

impl From<SqliteError> for MyError {
    fn from(err: SqliteError) -> MyError {
        // Cow implements From<&str> and From<String>
        // Type annotation may not be needed, just added for clarity
        let msg: Cow<'static, str> = match err.detail {
            Some(detail) => detail.into(),
            None => err.desc.into(),
        };

        MyError { msg: msg }
    }
}

#4

Great hints here, thanks!

Does anything speak against shortening this to let msg = format!("{}", err) since Errors already implement the Display trait?


#5

You can do that, too, or err.to_string() which is a shorthand. The Display impl includes the error code (self.kind as u32), if that’s acceptable.