如何为构建器提供可变引用,但只提供对构建对象的不可变引用?

时间:2016-07-28 17:14:23

标签: rust lifetime

构建FooBuilder时,我想提供&mut Bar。当我构建Foo时,我想提供&BarFoo应该可以从&self调用Bar方法。换句话说,可变借用只应在FooBuilder的生命期间存在。

struct FooBuilder<'a> {
    bar: &'a mut Bar,
}
impl<'a> FooBuilder<'a> {
    fn new(bar: &'a mut Bar) -> Self {
        FooBuilder { bar: bar }
    }
    fn build(&'a self) -> Foo<'a> {
        Foo { bar: &self.bar }
    }
}

struct Foo<'a> {
    bar: &'a Bar,
}

struct Bar;
impl Bar {
    fn bar(&self) {}
}

fn main() {
    let mut bar = Bar;
    let foo = FooBuilder::new(&mut bar).build();
    bar.bar();
}

此代码有错误:

error: borrowed value does not live long enough
  --> <anon>:24:15
   |
24 |     let foo = FooBuilder::new(&mut bar).build();
   |               ^^^^^^^^^^^^^^^^^^^^^^^^^ does not live long     enough
   |
note: reference must be valid for the block suffix following     statement 1 at 24:48...
  --> <anon>:24:49
   |
24 |     let foo = FooBuilder::new(&mut bar).build();
   |                                                 ^
note: ...but borrowed value is only valid for the statement at 24:4
  --> <anon>:24:5
   |
24 |     let foo = FooBuilder::new(&mut bar).build();
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: consider using a `let` binding to increase its lifetime
  --> <anon>:24:5
   |
24 |     let foo = FooBuilder::new(&mut bar).build();
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error[E0502]: cannot borrow `bar` as immutable because it is also     borrowed as mutable
  --> <anon>:25:5
   |
24 |     let foo = FooBuilder::new(&mut bar).build();
   |                                    --- mutable borrow occurs     here
25 |     bar.bar();
   |     ^^^ immutable borrow occurs here
26 | }
   | - mutable borrow ends here

error: aborting due to 2 previous errors

2 个答案:

答案 0 :(得分:2)

第一步是修复build

为了将&mut T转换为&T,您需要使用 &mut T(否则您会有别名和可变性)。这意味着:

  • 消费构建者,而不是参考它
  • 传递可变参考,而不是参考它

简而言之,你来自:

fn build(&'a self) -> Foo<'a> {
    Foo { bar: &self.bar }
}

为:

fn build(self) -> Foo<'a> {
    Foo { bar: self.bar }
}

这会给您留下一个错误:

error: cannot borrow `bar` as immutable because it is also borrowed as mutable [--explain E0502]
  --> <anon>:25:5
24 |>     let foo = FooBuilder::new(&mut bar).build();
   |>                                    --- mutable borrow occurs here
25 |>     bar.bar();
   |>     ^^^ immutable borrow occurs here
26 |>     //foo.bar.bar();
27 |> }
   |> - mutable borrow ends here

就编译器可以从方法签名中看到的那样,bar是可变的,因此无法直接使用。借用延长直至foo被删除。

此修复非常简单:不是直接使用bar,而是使用bar中的foo引用。或者说明范围很重要:

fn main() {
    let mut bar = Bar;
    {
        let foo = FooBuilder::new(&mut bar).build();
        // `bar` currently borrow (mutably) by `foo`, cannot use it directly
        foo.bar.bar();
    }
    // `bar` no longer borrowed, use at your heart's content
    bar.bar();
}

答案 1 :(得分:1)

如果您不介意bar Rc,请执行类似的操作。诀窍是,如果只有一个Rc引用,您可以获得对内容的&mut引用。这有点倒退;它不是在编译时将&mut降级为&,而是利用运行时信息(引用计数)来升级&#34;从不可变的引用变为可变的。

use std::rc::Rc;

struct FooBuilder<'a> {
    bar: &'a mut Rc<Bar>,
}
impl<'a> FooBuilder<'a> {
    fn new(bar: &'a mut Rc<Bar>) -> Self {
        FooBuilder { bar: bar }
    }
    fn f(mut self) -> Self {
        Rc::get_mut(self.bar).unwrap().mut_method();
        self
    }
    fn build(&'a self) -> Foo {
        Foo { bar: self.bar.clone() }
    }
}

struct Foo {
    bar: Rc<Bar>,
}

struct Bar;
impl Bar {
    fn bar(&self) {}
    fn mut_method(&mut self) {}
}

fn main() {
    let mut bar = Rc::new(Bar);
    let foo = FooBuilder::new(&mut bar).f().build();
    bar.bar();
}

Play link

使用Rc克隆构建Foo后,会有多个引用,稍后尝试获取mut引用会引起恐慌(或者至少返回None Rc::get_mut())。

这意味着你只能这样做一次;如果您希望第二个FooBuilder从同一个Foo创建第二个bar,那么它将无法正常工作,因为如果您有一个&mut T,则不允许任何其他引用lapply

但这有点笨拙,根据具体情况,可能有更好的方法来解决实际问题。

相关问题