Why does one non-consuming builder compile while another does not?

91 views Asked by At

I read the builder pattern and then tried to build 2 different builders (Header and Request) as follows:

use std::ascii::AsciiExt;

#[derive(PartialEq, Debug)]
pub struct Headers<'a> (pub Vec<(&'a str, String)>);

impl<'a> Headers<'a> {
    pub fn replace(&'a mut self, name: &'a str, value:&str) -> &mut Headers<'a> {
        self.0.retain(|&(key, _)|!name.eq_ignore_ascii_case(key));
        self.0.push((name, value.to_string()));
        self
    }
}

#[derive(PartialEq, Debug)]
pub struct Request<'a> {
    pub headers: Headers<'a>,
}

impl<'a> Request<'a> {
    pub fn header(&'a mut self, name: &'a str, value:&'a str) -> &mut Request<'a> {
        self.headers.replace(name, value);
        self
    }
}

Why does Header compile fine but Request fails with:

error[E0499]: cannot borrow `*self` as mutable more than once at a time
   --> src/api.rs:154:9
    |
153 |         self.headers.replace(name, value);
    |         ------------ first mutable borrow occurs here
154 |         self
    |         ^^^^ second mutable borrow occurs here
155 |     }
    |     - first borrow ends here
1

There are 1 answers

2
Matthieu M. On BEST ANSWER

You have an issue with your lifetimes: you are re-using the same lifetime ('a) for too many different things, so that when the compiler attempts to use a single lifetime for all those 'a you get a confusing error message.

The solution is simple: do not use 'a everywhere you can put a lifetime, but only where it's necessary.

It is unnecessary to use &'a mut self, the instance (self) does not need to have the same lifetime as the &str it contains! (and actually, cannot really):

impl<'a> Headers<'a> {
    pub fn replace(&mut self, name: &'a str, value: &str) -> &mut Headers<'a> {
        self.0.retain(|&(key, _)|!name.eq_ignore_ascii_case(key));
        self.0.push((name, value.to_string()));
        self
    }
}

impl<'a> Request<'a> {
    pub fn header(&mut self, name: &'a str, value: &str) -> &mut Request<'a> {
        self.headers.replace(name, value);
        self
    }
}