Can I shadow lifetimes?

I've had to rework some code to fix a few lifetime issues. Currently I have code that looks like this

pub trait BucketApi<'tx>
where
  Self: Sized,
{
  fn bucket<'a, T: AsRef<[u8]>>(&'a self, name: T) -> Option<BucketImpl<'a, 'tx>>;
}

/// Read-only Bucket
pub struct BucketImpl<'p, 'tx: 'p> {
  pub(crate) b: BucketCell<'tx>,
  p: PhantomData<&'p u8>,
}

and is used like

    let db = DB::new();
    let tx = db.begin()?; // db borrowed with lifetime 'tx
    let b = tx.bucket("a_bucket").unwrap(); // tx borrowed with lifetime 'p

The current API works fine, but I'd like to simplify the lifetime handling. Since 'tx: 'p in all cases, I tried to replace the 'tx lifetime with 'p so that there's only one lifetime to keep track of. The compiler gave an error that it requires 'p: 'tx, also.

Is there a way to simplify the lifetime handling so that I only need to keep track of one lifetime in the API?

Thanks!

If, as implied by the name, BucketCell has some kind of interior mutability, then it is invariant over 'tx, which means it cannot shrink to be smaller and thus you need two lifetimes.

3 Likes

Quickly skimming through the code, the lifetime does not appear to be invariant, although you could confirm that hypothesis with the following code:

/// Assert that `BucketCell<'tx>` is covariant over `'tx`
const _: () = {
    fn _check<'smol, 'big : 'smol>(
        it: BucketCell<'big>,
    ) -> BucketCell<'smol>
    {
        it
    }
};

Assuming things to be covariant over 'tx, you'll then need to ensure that 'tx : 'a.

  1. Let's rename 'a to 'bucket, to avoid the semantically-void 'a name.

  2. Add a where 'tx : 'bucket bound to the fn bucket() method in the trait definition:

    pub trait BucketApi<'tx> : Sized {
        fn bucket<'bucket>(&'bucket self, name: impl AsRef<[u8]>)
          -> Option<BucketImpl<'bucket>>
        where
            'tx : 'bucket,
        ;
    
  3. Use a single lifetime param, 'bucket, for your BucketImpl definition, since by covariance of BucketCell<'_>, its 'tx lifetime parameter ought to be able to shrink down to this 'bucket lifetime parameter.

    /// Read-only Bucket.
    pub struct BucketImpl<'bucket> {
        pub(crate) b: BucketCell<'bucket>,
    }
    

If the covariance check passes, but somehow you end up with lifetime errors down the line, then it means that some other API or usage does require "remembering how much bigger than 'bucket the original 'tx was".

2 Likes

You are correct. The underlying RefCell is the problem. Now I need to grock the underlying logic.

Thank you!

Unfortunately the covariance test fails. Under the covers a RefCell is used which is causing all of my variance problems!

Would you recommend renaming all of the lifetimes?

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.