I was struggling a bit to handle errors from the postgres
crate. Specifically, the SqlState
in postgres::error
. Since the constants are using Cow
, I did not manage to use it in a simple way in match patterns.
For example, UNIQUE_VIOLATION
is defined as
/// 23505
pub const UNIQUE_VIOLATION: SqlState = SqlState(Cow::Borrowed("23505"));
I ended up with doing the following, but I am sure there is a more ergonomic/canonical way of matching the errors.
let maybe_user: postgres::Result<User> = create_user(&conn, email);
match maybe_user {
Err(e) => {
// e.code() returns Option<&postgres::SqlState>
match e.code() {
None => panic!("not a sql error"),
Some(sqlstate) if sqlstate == &UNIQUE_VIOLATION => {
// do something...
},
Some(sqlstate) => panic!("unexpected error"),
}
}
Ok(user) => {
assert_eq!(user.email, email);
}
}
If I want to explicitly handle a large quantity of errors, it gets really tedious to repeat lines like
Some(sqlstate) if sqlstate == &UNIQUE_VIOLATION => {},
So, my question is, is there a simpler, more ergonomic way of matching on these errors?
The suggestion of @edwardw is what I wanted to achieve but everything I tried did not compile.
UNIQUE_VIOLATION
in the match pattern gives the error:expected &postgres_shared::error::sqlstate::SqlState, found struct postgres_shared::error::sqlstate::SqlState
&UNIQUE_VIOLATION
in the match patterns gives the error:error: to use a constant of type std::borrow::Cow in a pattern, std::borrow::Cow must be annotated with #[derive(PartialEq, Eq)]
Heres a minimal example to quickly reproduce the errors:
use postgres::error::UNIQUE_VIOLATION;
use postgres::Result;
fn example() {
let sql_state = &UNIQUE_VIOLATION;
match sql_state {
// this match arm works
s if s == &UNIQUE_VIOLATION => {
// do something...
}
// this match arm does not compile
&UNIQUE_VIOLATION => {},
_ => panic!("unexpected state"),
}
}
Requires postgres
as dependency in Cargo.toml
[dependencies]
postgres = "0.15.2"
No, I'm afraid that you can't do much better than this. One "obvious" improvement seems to be:
match e.code().expect("not a sql error") {
&UNIQUE_VIOLATION => {
// do something
},
_ => panic!("unexpected error"),
}
But it fails to compile with the error:
error: to use a constant of type
std::borrow::Cow
in a pattern,std::borrow::Cow
must be annotated with#[derive(PartialEq, Eq)]
The reason why it should fail isn't obvious. The Rust reference has thispassing sentence :
(...in a path patterns...) Struct and enum constants must have
#[derive(PartialEq, Eq)]
(not merely implemented).
Turns out this is deliberate on the part of rustc
. RFC 1445 explains why and how. In short,
When expanding constants of struct or enum type into equivalent patterns, require that the struct or enum type is decorated with
#[structural_match]
.
The feature gate #[structural_match]
will be kept unstable. This feature gate is only automatically applied if a struct or enum is marked as #[derive(Eq)]
but not if manually implemented. The std::borrow::Cow
struct, which is part of SqlState
you tried to match against, unfortunately falls into later category.
The ==
operator, however, is not subject to such restriction. So a constant struct or enum can still be used in match guards as you did in the question.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.