I am trying to return errors of multiple types from a method, one error being generic.
I'd like not to use the Box<dyn Error>
trick, mostly to learn how to achieve what I want to implement here.
The get function retrieves a value from the DB (which can fail with a rusqlite::Error
), and then parses the value with from_str
, which is defined on the generic return type of get.
enum GetError<T: std::str::FromStr> {
Error1(T::Err),
Error2(rusqlite::Error)
}
impl<T: std::str::FromStr> From<T::Err> for GetError<T> {
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
fn from(value: T) -> Self {
GetError::Error1(value)
}
}
impl<T: std::str::FromStr> From<rusqlite::Error> for GetError<T> {
fn from(value: rusqlite::Error) -> Self {
GetError::Error2(value)
}
}
pub fn get<T>(&self, key: &str) -> Result<T, GetError<T>>
where
T: std::str::FromStr,
{
match self.connection.query_row("SELECT value FROM key_values WHERE key = ?1", params![key], |row| { row.get::<usize, String>(0) }) {
Err(error) => Err(GetError::Error2(error)),
Ok(value) => T::from_str(&value).or_else(|e| GetError::Error1(e))
}
}
It's telling me that there is already an implementation of the trait From
defined in std::convert
.
error[E0119]: conflicting implementations of trait `std::convert::From<storage::permanent_key_value_store::GetError<_>>` for type `storage::permanent_key_value_store::GetError<_>`
--> src/storage/permanent_key_value_store.rs:13:1
|
13 | impl<T: std::str::FromStr> From<T::Err> for GetError<T> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: conflicting implementation in crate `core`:
- impl<T> std::convert::From<T> for T;
Is there a way to return a generic error GetError
that could stand for a rusqlite::Error
or any FromStr::Err
?
Thank you so much!
Now the code boils down to (using ?):
pub enum GetError<T: std::str::FromStr> {
Error1(T::Err),
Error2(rusqlite::Error)
}
impl<T: std::str::FromStr> From<rusqlite::Error> for GetError<T> {
fn from(value: rusqlite::Error) -> Self {
GetError::Error2(value)
}
}
pub fn get<T>(&self, key: &str) -> Result<T, GetError<T>>
where
T: std::str::FromStr,
{
let value = self.connection.query_row(
"SELECT value FROM key_values WHERE key = ?1",
params![key],
|row| { row.get::<usize, String>(0) }
)?;
T::from_str(&value).map_err(GetError::Error1)
}
The problem is that one can put whatever they want in their FromStr
's associated type Err
, for example:
struct Foo;
impl std::str::FromStr for Foo {
type Err = GetError<Foo>;
// ...
}
And then the From
impl you wrote for GetError<Foo>
will collide with the empty, provided-by-std impl<T> From<T> for T
impl.
You can avoid that by getting rid of this From
impl and constructing the error manually:
enum GetError<T: std::str::FromStr> {
Error1(T::Err),
Error2(rusqlite::Error),
}
impl<T: std::str::FromStr> From<rusqlite::Error> for GetError<T> {
fn from(value: rusqlite::Error) -> Self {
GetError::Error2(value)
}
}
pub fn get<T>(&self, key: &str) -> Result<T, GetError<T>>
where
T: std::str::FromStr,
{
match self.connection.query_row(
"SELECT value FROM key_values WHERE key = ?1",
params![key],
|row| row.get::<usize, String>(0),
) {
Err(error) => Err(GetError::Error2(error)),
Ok(value) => T::from_str(&value).map_err(|e| GetError::Error1(e)),
}
}