2016-07-28 32 views
4

构建FooBuilder时,我想提供一个&mut Bar。当我建立Foo我想提供一个&BarFoo应该能够从Bar调用&self方法。换句话说,可变借款只能在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 
+0

你的问题到底是什么? – antoyo

+3

这不是修改问题的最佳礼仪,它会使现有答案失效。 – Shepmaster

+0

我不认为这是可能的,对不起。您无法将可变引用降级为共享引用,以重新获得共享生存期。 – Veedrac

回答

1

你可以做同样的事情,如果你不介意在Rc包装bar。诀窍是,如果只有一个Rc参考,您可以获得&mut对内容的参考。这有点倒退;而不是在编译时将&mut降级为&,而是使用运行时信息(引用计数)将不可变引用“升级”为mutable。

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克隆,有多于一个参考,并稍后尝试进入一个mut参考将会崩溃(或至少回报NoneRc::get_mut())。

这意味着你只能做一次;如果您希望第二个FooBuilder从同一个bar创建第二个Foo它不起作用,因为如果您有&mut T,您不允许任何其他参考。

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

+1

如果你正在动态检查,一个'RefCell'重量更轻。 https://play.rust-lang.org/?gist=88a746b54a6cd6e6ede1818c8ce136d2&version=stable&backtrace=0 – Veedrac

+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

我想你已经误解了这个问题;我的理解是,你最后一步 - 在使用'bar'之前丢弃'foo' - 违反了OP的愿望。 OP希望'foo'和'bar'能够一起使用。 – Veedrac

+0

@Veedrac:这不是我从这个问题中得到的,但我现在看到它也可以用这种方式来阅读。让我们来看看OP会如何看待这个答案。 –

相关问题