简体   繁体   中英

How do I implement a trait for all lifetimes of the struct?

I reduced my problem to this minimum reproducible example ( playground ):

use std::marker::PhantomData;

pub trait D<'a> {
    type A: A + 'a;
    type B: B + 'a;
}

pub trait A {
    fn a(self);
}

pub trait B {
    fn b(self);
}

pub struct AA {}

impl A for AA {
    fn a() {}
}

pub struct BB<'a> {
    phantom: PhantomData<&'a u8>,
}

impl<'a> B for BB<'a> {
    fn b() {}
}

impl<'a: 'd, 'd> D<'d> for E<'a> {
    type A = AA;
    type B = BB<'a>;
}

#[derive(Clone)]
pub struct E<'a> {
    phantom: PhantomData<&'a u8>,
}

pub struct S<D_T>
where
    D_T: for<'d> D<'d>,
{
    a: D_T,
}

pub enum R<'a> {
    RR(S<E<'a>>),
}

I'm getting

error: implementation of `D` is not general enough
  --> src/lib.rs:48:8
   |
3  | / pub trait D<'a> {
4  | |     type A: A + 'a;
5  | |     type B: B + 'a;
6  | | }
   | |_- trait `D` defined here
...
48 |       RR(S<E<'a>>),
   |          ^^^^^^^^ implementation of `D` is not general enough
   |
   = note: `D<'0>` would have to be implemented for the type `E<'a>`, for any lifetime `'0`...
   = note: ...but `D<'_>` is actually implemented for the type `E<'1>`, for some specific lifetime `'1`

I have a small idea why it's happening, but is there a way to have BB with a reference in this case? Most of the things here are from a library I don't control so I cannot change them.

The struct S would need D to be implemented for D_T for all lifetimes, which would also include 'static . Now, S is used in enum R with type E , with a generic lifetime 'a . If 'a is anything but 'static , type B of struct D could not possibly be implemented containing a non-static reference. This simply can not work, and something needs changing.

The first option would be to make enum R use 'static lifetime in E , but that would be rather unpractical.

Another option would be to remove the D_T: for<'d> D<'d> requirement from S , adding an additional lifetime argument, and adding phantom data to the structure.

If neither of them are possible, and I am not missing anything, there is not much you can do without resorting to runtime checks, and making the BB type not be lifetime bound (using reference counting). You would need to use either Rc<BB> as B , or have a Rc<SomeType> inside the BB . The former would look something like this:

impl<'d> D<'d> for E {
    type A = AA;
    type B = Rc<BB>;
}

#[derive(Clone)]
pub struct E {
    ref_counted: Rc<BB>
}

pub struct BB {

}

impl B for Rc<BB> {
    fn b(mut self) {
        if let Some(bb) = Rc::get_mut(&mut self) {
            //code with BB's MUTABLE REFERENCE
        }
    }
}


pub struct S<D_T>
where
    D_T: for<'d> D<'d>,
{
    a: D_T
}


pub enum R {
    RR(S<E>),
}

The B implementation uses a mutable reference of the underlying value in the method, if you would like to consume the value inside ref_counted instead, you would have to try something like this:

impl B for Rc<BB> {
    fn b(mut self) {
        match Rc::try_unwrap(self) {
            Ok(bb) => {
                //code with BB's VALUE
            },
            Err(bbrc) => {
                //we can not consume the value in self, because
                //there was a reference held somewhere else in the code
                //here lies code with BB's reference counted pointer
            }
        }
    }
}

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM