Can't figure out how to use lifetime in trait without explicit lifetime definition

Hi, I'm trying to write a diesel extension for postgis types. For that purpose I use rust-postgis library, because it already has all necessary types and transformations. I want to avoid a copy of data whenever possible and reuse datatypes from that library. I saw the implementation done here, but it doesn't look generic enough. So I started from defining the Point too, but did it like that

#[derive(Copy, Clone, PartialEq, FromSqlRow, AsExpression)]
#[sql_type = "Geometry"]
pub struct Point<P>
{
    pub point: P,
}

Than there was no any problem yet for from_sql method, yes I still may not understand some concepts, that is my second library on Rust, but it works for some cases:

impl<P> FromSql<Geometry, Pg> for Point<P>
where
    P: postgis::Point+EwkbRead,
{
    fn from_sql(bytes: Option<&<Pg as Backend>::RawValue>) -> deserialize::Result<Self> {
        let bytes = not_none!(bytes);
        let mut r = Cursor::new(bytes);
        let geom = ewkb::GeometryT::read_ewkb(&mut r)?;
        return match geom {
            postgis::ewkb::GeometryT::Point(p) => Ok(Point { point: p }),
            _ => Err(format!("Geometry is not a point").into()),
        };
    }
}

But problem appears when I try to implement the to_sql method, because AsEwkbPoint requires lifetime definition. I tried that code and code without lifetime definition and code with AsEwkbPoint<'_>, that is how I tried with defined lifetimes

impl<'a, P> ToSql<Geometry, Pg> for Point<P>
    where
        P: postgis::Point + AsEwkbPoint<'a>{
    fn to_sql<W: Write>(&'a self, out: &mut Output<W, Pg>) -> serialize::Result {
        self.point.as_ewkb().write_ewkb(out)?;
        Ok(IsNull::No)
    }
}

But I have that error

   |
77 |     fn to_sql<W: Write>(&'a self, out: &mut Output<W, Pg>) -> serialize::Result {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetime mismatch
   |
   = note: expected fn pointer `fn(&Point<P>, &mut diesel::serialize::Output<'_, W, Pg>) -> std::result::Result<_, _>`
              found fn pointer `fn(&'a Point<P>, &mut diesel::serialize::Output<'_, W, Pg>) -> std::result::Result<_, _>`
note: the anonymous lifetime #1 defined on the method body at 77:5...
  --> src/lib.rs:77:5
   |
77 |     fn to_sql<W: Write>(&'a self, out: &mut Output<W, Pg>) -> serialize::Result {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...does not necessarily outlive the lifetime `'a` as defined on the impl at 74:6
  --> src/lib.rs:74:6
   |
74 | impl<'a, P> ToSql<Geometry, Pg> for Point<P>
   |      ^^

I understand that if I define the struct which implements AsEwkbPoint in Point struct it'll work, but I don't want to do that in library, I believe that it should be defined in runnable code for specific table structure like so

#[derive(Insertable)]
#[table_name = "geometry_samples"]
struct NewGeometrySample {
    point: postgis_diesel::Point<postgis::ewkb::Point>,
}

Please help to understand how to solve issues with lifetime like that one.

You'll have to use HRTB for<'a> AsEwkbPoint<'a>, because you can't change the interface of the trait.

1 Like

No way, so simple :man_facepalming:, never thought that I can do it like this in where definition

impl<P> ToSql<Geometry, Pg> for Point<P>
    where
        P: postgis::Point + for <'a> AsEwkbPoint<'a>{
    fn to_sql<W: Write>(&self, out: &mut Output<W, Pg>) -> serialize::Result {
        self.point.as_ewkb().write_ewkb(out)?;
        Ok(IsNull::No)
    }
}