The rustc refuses 1 of 3 seemingly identical ways to write same code. Bug?

Check out the following code, rustc refuses one of the three seemingly identical ways to define fn process. I failed to reason why. Help?

#[cfg(also_work)]
type MyFn<'a> = dyn FnMut() + 'a;

#[cfg(not_work)]
type MyFn = dyn FnMut();

#[cfg(any(also_work, not_work))]
fn process(f: &mut MyFn)
{
    for _ in 0..5 {
        f();
    }
}

#[cfg(not(any(also_work, not_work)))]
fn process(f: &mut dyn FnMut())
{
    for _ in 0..5 {
        f();
    }
}

fn main() {
    let mut v = Vec::<&str>::new();

    process(&mut || {
        v.push("A");
    });

    println!("v={:?}", v);
}

This is the same as dyn FnMut() + 'static, but the closure that you pass into process is not 'static (it has a reference tov on which is on the stack). So the reference to the closure cannot be coerced into a reference to a dyn FnMut() + 'static.

When you add dyn FnMut() + 'a you are removing the 'static constraint, so you can use it to refer to things on the stack.

I'm not sure why no type alias works, I think it has to do with the lifetime elision rules. That makes it equivalent to the case of dyn FnMut() + 'a.

Thanks for the reply. That no alias variant is especially surprising. If the lifetime elision rule works in its favor, why not the type alias without explicit lifetime one? Syntactically they are identical to me.

I also don't understand why type MyFn = dyn FnMut(); is equivalent to dyn FnMut() + 'static.

Yes, I also find that the no alias variant surprising, but nice. I understand why the type alias doesn't work, Rust doesn't like to have implicit lifetimes outside of function arguments and return types, otherwise it gets too confusing. For example, the lifetime could be nested a few libraries deep, and you could have no idea that lifetimes are involved, and get really confusing lifetimes errors. With functions, this is mitigated because you can just look at the type (1 level of indirection), but with type aliases, there is no limit to how deep it can go.

If you don't specify a lifetimes for a trait object, it gets 'static as a rule. This lifetimes specifies how long the concrete type behind the trait object must live. For example, you can coerce a &i32 to a &dyn Trait because i32: 'static, but you can't coerce a &&i32 to a &dyn Trait because &i32: 'static may not hold.

Appreciated, I guess rule is the rule.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.