I have a struct containing &str
with the same lifetime annotation as the struct itself. When I try to call the first::<T>(None)
method on a worker::d1::D1PreparedStatement
, which is supposed to query my database and parse the values into type T
, I get "implementation of 'Deserialize' is not general enough".
Here some of my code:
use worker::;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
pub struct User<'a> {
#[serde(with = "uuid_as_string")]
uuid: Uuid,
name: &'a str,
display_name: &'a str,
profile_text: &'a str,
profile_picture_url: &'a str,
email_address: &'a str,
password: &'a str,
created: usize,
deleted: usize,
timezone: isize,
}
mod uuid_as_string {
use serde::{self, Deserialize, Deserializer, Serializer};
use uuid::Uuid;
pub fn serialize<S>(uuid: &Uuid, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&uuid.to_string())
}
pub fn deserialize<'a, D>(deserializer: D) -> Result<Uuid, D::Error>
where
D: Deserializer<'a>,
{
let s = String::deserialize(deserializer)?;
Uuid::parse_str(&s).map_err(serde::de::Error::custom)
}
}
#[event(fetch, respond_with_errors)]
pub async fn main(request: Request, env: Env, _ctx: Context) -> Result<Response> {
Router::new()
.getasync("/", |, ctx| async move {
let d1 = ctx.env.d1("D1_DATABASE")?;
let statement = d1.prepare("SELECT FROM users");
match statement.first::<User>(None).await? {
Some(user) => Response::from_json(&user),
None => Response::error("Not found", 404),
}
})
.run(request, env)
.await
}
I tried completely removing the Uuid
field and the module for handling it and it didn't change anything.
The problem is that you can't do zero-copy deserialization in this case. The signature of
workers::d1::D1PreparedStatement::first()
is as follows:That
where T: for<'a> Deserialize<'a>
means that the deserialization must be owned (note that this is exactly howDeserializeOwned
is implemented), and therefore "can be deserialized without borrowing any data from the deserializer."You must remove the borrowing from your struct to use it in this context, perhaps by replacing the
&'a str
fields with ownedString
s.