Unhelpful "mismatched types" error message

Hi all, I am trying to pass a structure field to a "mutable" method.
I get a mismatched types error message (line 22) on the following code. I think it is actually a lifetime error but I cannot solve it.

Is my code fixable or is there another way to do what I want ?

pub struct Emulator {
    a: u8
}

impl Emulator {
    fn new() -> Emulator {
        Emulator {
            a: 0,
        }
    }

    fn alu_rl<'a, F: FnMut(&mut Emulator) -> &mut u8>
        (&'a  mut self, mut callback: F) {
            let src = callback(self);
    }
}

fn main() {
    let emu = Emulator::new();
    let closure = |emu: &mut Emulator| &mut emu.a;

    emu.alu_rl(closure);
}

The issue here is that the elided lifetime for closures is not the same as the elided lifetime for functions. The function alu_rl desugar to

    fn alu_rl<'a, F>(&'a mut self, mut callback: F)
    where
        F: for<'b> FnMut(&'b mut Emulator) -> &'b mut u8,
    {
        let src = callback(self);
    }

whereas your closure desugar to

// not valid syntax
let closure = for<'a, 'b> |emu: &'a mut Emulator| &'b mut emu.a;

As you can see, your function expects a closure with a same lifetime for the input argument and return value however the closure you're passing as an argument has a different lifetime for both. The only way to specify a lifetime in your situation is to pass the closure through a function, then update your main:

fn constrained<T, U, F>(f: F) -> F
where
    F: FnMut(&mut T) -> &mut U,
{
    f
}

fn main() {
    let mut emu = Emulator::new();
    let closure = constrained(|emu: &mut Emulator| &mut emu.a);
    emu.alu_rl(closure);
}

You can find a better explanation of the issue here

Note that by directly passing the closure to your function and not saving it to a temporary variable, rustc will correctly infer lifetimes though you might not always be able to do this:

fn main() {
    let mut emu = Emulator::new();
    emu.alu_rl(|emu: &mut Emulator| &mut emu.a);
}
1 Like

Thanks the trick with the constrained function works perfectly !
When debugging and putting lifetimes everywhere I couldn't put them on the closure, now I know more on how it works :slight_smile:
Sadly I cannot directly pass the closure to the function but it is nice to know.

1 Like