Why 'b: 'a is needed here?

The code

trait Ts<'a>: 'a {}
impl<'a, T: 'a> Ts<'a> for T {}

fn a<'b, 'a: 'b>(_n: &'a mut i32, _m: &'b mut i32) {
    static mut E: i32 = 9;
    let _c: &'a mut (dyn Ts<'b> + 'a) = unsafe { &mut *std::ptr::addr_of_mut!(E) };
}

got the error:

  |
4 | fn a<'b, 'a: 'b>(_n: &'a mut i32, _m: &'b mut i32) {
  |      --  -- lifetime `'a` defined here
  |      |
  |      lifetime `'b` defined here
5 |     static mut E: i32 = 9;
6 |     let _c: &'a mut (dyn Ts<'b> + 'a) = unsafe { &mut *std::ptr::addr_of_mut!(E) };
  |             ^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'b` must outlive `'a`
  |
  = help: consider adding the following bound: `'b: 'a`

Why 'b:'a is required?

Ts<'b> has a bound Self: 'b, so dyn Ts<'b> has some lifetime which at least is not shorter than 'b. Hence 'a: 'b

Then (dyn Ts<'b> + 'a) as a whole keeps alive while 'a is in scope.
So, to my understanding, no 'b: 'a should be required to keep the code sound

You have to think about it this way:

When you write &'a Something or &'a mut Something, you are borrowing Something for a specific amount of time ('a), therefore Something must outlive 'a.

But if T: 'a, then there is no need of other bounds to make &'a T sound.

dyn Ts<'b> + 'a means a trait object with lifetime 'a, it is 'a already.

Yes, but the lifetime in the trait Ts<'b> creates an implied bound. Maybe reading this could help you: Trait bound interactions - Learning Rust

The implied bound it created is 'a : 'b, and it adds nothing more because this bound has already specified

No. The implied bound it creates is 'b : 'a.

Ok, let's check the example and mine

pub trait LifetimeTrait<'a, 'b>: 'a {}

fn fp<'a, 'b, 'c>(t: Box<dyn LifetimeTrait<'a, 'b> + 'c>) {
    // This compiles which indicates an implied `'c: 'a` bound
    let c: &'c [()] = &[];
    [...]
}

// ====================

trait Ts<'a>: 'a {}
fn a<'b, 'a: 'b>(_n: &'a mut i32, _m: &'b mut i32) {
    let _c: &'a mut (dyn Ts<'b> + 'a)  = ...
}

An implied 'c: 'a in dyn LifetimeTrait<'a, '_> + 'c
An implied 'a: 'b in dyn Ts<'b> + 'a

There is no difference

Maybe this example helps:

pub trait LifetimeTrait<'a>: 'a {}

struct MyStruct;

impl<'a> LifetimeTrait<'a> for MyStruct {}

fn fp<'b, 'a: 'b>(t: &'a mut Box<dyn LifetimeTrait<'b>>) {
    
}

fn main() {
    let my_struct = MyStruct {};
    
    let mut my_box: Box<dyn LifetimeTrait> = Box::new(my_struct);
    
    fp(&mut my_box);
}

It will fail to compile because of the implied bound.

The only bounds (explicit or implicit) on the inputs to the function are 'a: 'b, but the appearance of 'b underneath a &'a mut requires 'b: 'a.

It's just a noisier version of this:

fn a<'b, 'a: 'b>(_n: &'a mut i32, _m: &'b mut i32) {
    let _c: &'a &'b [(); 0] = &&[];
}

Trying to construct a type with lifetime requirements not expressed in the function signature.

Even with &'r (dyn Ts<'b> + 'static), 'b: 'r is required. The outlives relation is syntactic.

That's the crux of this topic as far as I can tell.

Can this be changed? I don't think so, certainly not without some breakage and peril.

  • Breakage: &'r (dyn Ts<'b> + 'a) in a signature introduces an implied 'b: 'r bound that would have to go away, breaking some code directly, and the presumed fix (an explicit bound) would change some functions to no longer have higher-ranked implementations of the Fn traits, potentially breaking more code, ...

  • Peril: Unsafe code may be relying on the 'b: 'r requirement (and/or implied bounds) in order to be sound themselves

It's correct that dyn Ts<'b> + 'a in the signature wouldn't change anything since you already have the explicit 'a: 'b bound.

It's not the dyn Ts<'b> + 'a that's problematic, it's the &'a mut dyn Ts<'b>.

1 Like

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.