2
votes

I'm trying to implement a simple builder but struggling with lifetimes. The following is giving error: borrowed value does not live long enough. This question seems similar. If I store t in a mutable variable and then call s and finalize it works, but I want to get the one liner to work. What am I doing wrong?

struct Type<'a> {
    s: &'a String,
}

struct TypeBuilder {
    s: String,
}

impl TypeBuilder {
    fn new() -> TypeBuilder {
        TypeBuilder { s: "".to_string() }
    }

    fn s(&mut self, s: String) -> &mut TypeBuilder {
        self.s = s;
        self
    }

    fn finalize(&self) -> Type {
        Type { s: &self.s }
    }
}

fn main() {
    let t = TypeBuilder::new()
                    .s("a".to_string())
                    .finalize();
    println!("string: {}", t.s);
}
1

1 Answers

11
votes

The problem is that you're creating Type with a string slice based on a String from TypeBuilder, but TypeBuilder instance created with new() is destroyed immediately in the same let statement, so if this was allowed, the string slice would became dangling. And that's why it works when you store TypeBuilder in a variable first.

The problem with your approach to the builder is that the builder is the owner of data for the value it builds: Type references the contents of TypeBuilder. This means that Type instances are always tied to TypeBuilder instances, and you just cannot create Type and drop TypeBuilder. However, this is really unnatural - builders are usually transient objects which are only necessary during construction.

Consequently, in order for the builder pattern to work correctly your Type must become the owner of the data:

struct Type {
    s: String,
}

Then the builder should be passed by value and then consumed by finalize():

impl TypeBuilder {
    fn new() -> TypeBuilder {
        TypeBuilder { s: "".to_string() }
    }

    fn s(mut self, s: String) -> TypeBuilder {
        self.s = s;
        self
    }

    fn finalize(self) -> Type {
        Type { s: self.s }
    }
}

This way your building code should work exactly as it is.