Search code examples
rustlifetimetrait-objects

How to find generic bounds for producer that creates objects with trait-lifetime bounds


I have reduced my actual code to this minimal example:

trait Producer<P> where P: Something {
    fn produce(&self) -> Box<P>;
}

struct P1 {}

impl Producer<B1> for P1 {
    fn produce(&self) -> Box<B1> {
        Box::new(B1 {})
    }
}

trait Something {}

trait Borrower<'b> {
    type B: std::fmt::Display;
    
    fn borrow(&'b self) -> Self::B;
}

struct B1 {}

impl Something for B1 {}

impl<'b> Borrower<'b> for B1 {
    type B = Borrowing1<'b>;
    
    fn borrow(&'b self) -> Self::B {
        Borrowing1 { _b: &self }
    }
}

struct Borrowing1<'b> {
    _b: &'b B1,
}

impl<'b> std::fmt::Display for Borrowing1<'b> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "Borrowing1")
    }
}

fn perform<'b, P, B>(producer: P) where P: Producer<B>, B: Something + Borrower<'b> + 'static {
    for _ in 0..1 {
        let b = producer.produce();
        let s = b.borrow().to_string();
        eprintln!("{}", s);
    }
}

fn main() {
   let p1 = P1 {};
   perform(p1);
}

I have a Producer type that creates Something. And that something can implement Borrower<'b>, which introduces a lifetime. Then, I want to restrict a function perform to receive producers that produce Something with the trait Borrower<'b>. However, since I can't prevent introducing a lifetime in perform the function thinks that all produced items must live for the entire function execution. Actually, they are static objects that just implement Borrower<'b>. But I struggle finding the correct bounds.

The error message reflects that:

error[E0597]: `*b` does not live long enough
  --> src/main.rs:46:17
   |
43 | fn perform<'b, P, B>(producer: P) where P: Producer<B>, B: Something + Borrower<'b> + 'static {
   |            -- lifetime `'b` defined here
...
46 |         let s = b.borrow().to_string();
   |                 ^---------
   |                 |
   |                 borrowed value does not live long enough
   |                 argument requires that `*b` is borrowed for `'b`
47 |         eprintln!("{}", s);
48 |     }
   |     - `*b` dropped here while still borrowed

Maybe you can help me with that.


Solution

  • This can be solved using higher-rank trait bounds:

    You don't have to pass a lifetime to function here. Instead you need to define a lifetime on the go when borrow() is called. Following should solve the issue:

    fn perform<P, B>(producer: P)
    where
        P: Producer<B>,
        for<'b> B: Something + Borrower<'b> + 'static,
    {
        for _ in 0..1 {
            let b = producer.produce();
            let u = b.borrow();
            let s = u.to_string();
            eprintln!("{}", s);
        }
    }
    

    Playground

    Higher-rank trait bounds are explained pretty nicely here:
    How does for<> syntax differ from a regular lifetime bound?