Search code examples
rustlifetime

explicit lifetime for self in trait seems to cause "E0499 cannot borrow `*emitter` as mutable more than once at a time" in a loop


I ran into a problem with a trait that can return a reference to data that lives in self. Here is a minimal example case:

trait A<T> {
    fn emit_symbol(&mut self) -> T;
}

fn via_ans<'a>(emitter: &'a mut impl A<char>) -> String {
    let mut rval = String::new();
    for _ in 0..30 {
        let ch = emitter.emit_symbol();
        rval.push(ch)
    }
    rval
}

struct Hippo<T> {
    things: Vec<T>,
}

impl<T> A<&T> for Hippo<T> {
    fn emit_symbol(&mut self) -> &T {
        &self.things[2]
    }
}

gives error

error: `impl` item signature doesn't match `trait` item signature
  --> src/b.rs:19:5
   |
2  |     fn emit_symbol(&mut self) -> T;
   |     ------------------------------- expected `fn(&'1 mut b::Hippo<T>) -> &'2 T`
...
19 |     fn emit_symbol(&mut self) -> &T {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found `fn(&'1 mut b::Hippo<T>) -> &'1 T`
   |
   = note: expected signature `fn(&'1 mut b::Hippo<T>) -> &'2 T`
              found signature `fn(&'1 mut b::Hippo<T>) -> &'1 T`
help: the lifetime requirements from the `impl` do not correspond to the requirements in the `trait`
  --> src/b.rs:2:34
   |
2  |     fn emit_symbol(&mut self) -> T;
   |                                  ^ consider borrowing this type parameter in the trait

If I add a lifetime to the trait for &'s self then the implementation works, but I encounter a new error in the loop that uses the trait's method.

trait A<'s, T> {
    fn emit_symbol(&'s mut self) -> T;
}

fn via_ans<'a: 'b, 'b>(emitter: &'a mut impl A<'b, char>) -> String {
    let mut rval = String::new();
    for _ in 0..30 {
        let ch = emitter.emit_symbol();
        rval.push(ch)
    }
    rval
}

struct Hippo<T> {
    things: Vec<T>,
}

impl<'s, T> A<'s, &'s T> for Hippo<T> {
    fn emit_symbol(&'s mut self) -> &'s T {
        &self.things[2]
    }
}

gives error

error[E0499]: cannot borrow `*emitter` as mutable more than once at a time
  --> src/main.rs:10:18
   |
7  | fn via_ans<'a: 'b, 'b>(emitter: &'a mut impl A<'b, char>) -> String {
   |                    -- lifetime `'b` defined here
...
10 |         let ch = emitter.emit_symbol();
   |                  ^^^^^^^--------------
   |                  |
   |                  `*emitter` was mutably borrowed here in the previous iteration of the loop
   |                  argument requires that `*emitter` is borrowed for `'b`

Is this a shortcoming of Rust's borrow checker, or is there something else going on?

How can I adjust this trait to work in my application?


Solution

  • The problem with your second approach is that because impl A<'b, char> is a parameter to your function the compiler can only choose a single lifetime for 'b and as it's passed in from outside the function that lifetime also lasts for your whole function. So when you borrow emitter in the first call to emit_symbol that borrow is not released until after the function.

    This is a typical application for a Higher Ranked Trait Bound (HRTB):

    fn via_ans<'a>(emitter: &'a mut impl for<'b> A<'b, char>) -> String {
        let mut rval = String::new();
        for _ in 0..30 {
            let ch = emitter.emit_symbol();
            rval.push(ch)
        }
        rval
    }
    

    as that now means emitter has to be of a type that implements A for every lifetime, so the compiler can choose a different one for every loop iteration.

    In fact your problem is almost the same that serde sometimes has to solve with their Deserialize trait, to hide the not so pretty HRTB they add an additional trait DeserializeOwned which you can apply to your case as well:

    trait AOwned<T>: for<'a> A<'a, T> {}
    impl<S, T> AOwned<T> for S where S: for<'a> A<'a, T> {}
    fn via_ans<'a>(emitter: &'a mut impl AOwned<char>) -> String {
        //…
    }