生成器模式 - 借用的值存活时间不足

3
我想实现一个简单的构建器,但是在处理生命周期时遇到了困难。以下代码会出现 "borrowed value does not live long enough" 的错误。这个问题看起来与这个问题类似。如果我将 t 存储在可变变量中并调用 s 和 finalize,则可以正常工作,但我希望让这个一行代码正常工作。我做错了什么?
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);
}

我针对这个常见的 Rust 错误 error[E0716] error E0716: temporary value dropped while borrowed (rust) 提出了一个简明扼要的问题。它链接回这个问题。 - JamesThomasMoon
1个回答

12
问题在于你使用基于来自TypeBuilder的String的字符串切片创建了一个Type,但是用new()创建的TypeBuilder实例在同一let语句中立即被销毁,所以如果允许这样做,字符串切片就会变得悬空。这就是为什么先将TypeBuilder存储在变量中时它能正常工作的原因。
你实现的builder存在问题,因为builder拥有其构建的值的数据:Type引用了TypeBuilder的内容。这意味着Type实例始终与TypeBuilder实例相关联,你不能创建Type并丢弃TypeBuilder。然而,这真的很不自然-构建器通常是仅在构建过程中需要的瞬态对象。
因此,为了使builder模式正常工作,你的Type必须成为数据的所有者:
struct Type {
    s: String,
}

然后应该通过值传递构建器,然后在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 }
    }
}

这样做,您的建筑代码应该会完全按照原样工作。


1
太棒了,谢谢。实际上这与我开始的相似,但我遇到了问题,我认为是因为我在我的修改器函数中使用了“&mut self”,而不是使用“mut self”进行消耗。 - anderspitman
你知道为什么在这里使用&mut self吗?(http://doc.rust-lang.org/1.0.0-beta/book/method-syntax.html#builder-pattern) - anderspitman
5
在这种情况下,使用&mut self/&self的方法是可行的,因为该构建器仅适用于Copy数据,并且由构建器构建的结构体不包含对构建器的引用。但对于String和其他类型的拥有非可复制数据的情况,此方法将无法正常工作。 - Vladimir Matveev

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接