"Trait alias" causing lifetime error

Consider the following code:

pub type Callback = FnMut();

struct M<'a> {
    cb: &'a Callback
}

fn main() {
    let mut t = 3;
    let mut tr = &mut t;
    let m = M { cb: &|| { *tr = 1; } };
}

It doesn't compile. The compiler throws the following lifetime related errors:

error[E0597]: `t` does not live long enough
  --> src/main.rs:11:23
   |
11 |     let mut tr = &mut t;
   |                       ^ borrowed value does not live long enough
12 |     let m = M { cb: &|| { *tr = 1; } };
13 | }
   | - borrowed value only lives until here
   |
   = note: borrowed value must be valid for the static lifetime...

error[E0373]: closure may outlive the current function, but it borrows `tr`, which is owned by the current function
  --> src/main.rs:12:22
   |
12 |     let m = M { cb: &|| { *tr = 1; } };
   |                      ^^    -- `tr` is borrowed here
   |                      |
   |                      may outlive borrowed value `tr`
help: to force the closure to take ownership of `tr` (and any other referenced variables), use the `move` keyword
   |
12 |     let m = M { cb: &move || { *tr = 1; } };

However if I make a small change by replacing Callback with FnMut(). It compiles just fine. Can anyone please explain this behaviour?

I know that trait aliases are not a thing yet. I was using the above alias assuming they were allowed and now I know they aren't. Still, in the absence of support for trait aliases I'd have expected the compiler to throw some other error than something to do with lifetimes. Also why is it demanding a static lifetime for variable t?

Rust playground link here.

correct me if i'm wrong but i think the compiler makes type Callback = FnMut() + 'static out of it. at least if you use type Callback<'a> = FnMut() + 'a it it compiles

@juggle-tux Yup. You're right. It's using the 'static lifetime by default. But why so? Because it could not have possibly chosen any other lifetime? Why did it have to add a lifetime bound at all? I know that this probably some unintended behaviour I've triggered, but it's still interesting to understand.

A closure can borrow objects in its environment. It's the lifetimes of those borrowed objects that you're pinning down with the + 'a.

Aha. So that's the explanation. I have two questions still:

  1. Why does the error go away when I use FnMut() trait directly in the struct? Doesn't the 'static default bound apply in that case?
  2. Is the trait aliasing syntax like I've used above legal in current Rust?

Because the default object bound for &'a SomeTrait is &'a (SomeTrait + 'a).

It’s a type alias, and yes, it’s legal as you wrote it. If your closure didn’t capture anything from its environment (thus upholding the 'static requirement), the code would’ve compiled.

Trait alias is actually a somewhat related but different feature itself.

Thanks @vitalyd. That clears it up :slight_smile: