如何在循环中使用链式构建器模式而不会产生编译错误?

4
如何正确使用期望在循环中使用方法链接的构建器模式?以log4rs的示例为例。请注意,在appender中,self不是引用。
//builder pattern from log4rs

pub struct ConfigBuilder {
    appenders: Vec<Appender>,
    loggers: Vec<Logger>,
}

impl ConfigBuilder {
    pub fn appender(mut self, appender: Appender) -> ConfigBuilder {
        self.appenders.push(appender);
        self
    }
}

以下操作会导致错误,因为(我认为)cb被移动到由.appender()返回的内存中。
let cb = ConfigBuilder::new();
for x in ys {
    cb.appender(x);
}

这个看起来是可行的。这是唯一的方法吗?

let mut cb = ConfigBuilder::new();
for x in ys {
    cb = cb.appender(x);
}

你已经让 appender 接管并返回新的构建器,因此你需要将返回值放在某个地方。你是有意选择这种设计的吗?如果你想让第一个示例循环起作用,你需要传递一个可变的 self 引用。 - loganfsmyth
@loganfsmyth - 抱歉如果我表述不清楚,那个例子是来自log4rs包。https://crates.io/crates/log4rs。这不是我的设计。 - marathon
请参见 https://dev59.com/t5Lea4cB1Zd3GeqPzTjz - Shepmaster
你能发布完整的错误信息吗?这将非常有帮助。 - Lukas Kalbertodt
可能是将变量重复传递给改变该变量的函数的重复问题。 - Peter Hall
1个回答

9

这是唯一的方法吗?

从语义上讲,这是关键的方法,尽管有其他的写法。函数appender采用mut self方式,因此它将取得cb变量的所有权,并在那之后使该变量不可用。它也可以被设计为借用一个引用,但链式调用更方便。由于您处于循环中,因此构建器需要在下一次迭代中可用,所以您需要将该值分配给一个新的变量。这意味着:

let mut cb = ConfigBuilder::new();
for x in ys {
    cb = cb.appender(x);
}

确实,这是一种方法。另一种方法是使用Iterator.fold来进行操作。

let cb = ys.into_iter()
  .fold(ConfigBuilder::new(), |cb, x| cb.appender(x));

这种方法将所有内容都保存在一个作业中,但其他方面基本相同。


获取所有权和返回所有权是否会导致很多不必要的移动,还是编译器足够聪明,在这种情况下将对象留在原地? - kaya3

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