dakom
May 7, 2019, 8:46am
1
Why doesn't this work? I'd think that the mutable borrow ends when the nested function finishes, so that it's now available for an immutable borrow in the outer function:
Playground
struct Foo{}
impl Foo {
fn new() -> Self {
Self {}
}
fn as_mutable(&mut self) {
}
fn as_immutable(&self, _:()) {
}
}
fn main() {
let mut foo = Foo::new();
//This works fine
foo.as_mutable();
foo.as_immutable(());
//This fails
foo.as_immutable(foo.as_mutable());
}
The error is:
|
25 | foo.as_immutable(foo.as_mutable());
| --- ------------ ^^^^^^^^^^^^^^^^ mutable borrow occurs here
| | |
| | immutable borrow later used by call
| immutable borrow occurs here
ZyX-I
May 7, 2019, 10:34am
2
I am also wondering how the reverse happens to work:
Playground
struct Foo{}
impl Foo {
fn as_mutable(&mut self, _:()) {
}
fn as_immutable(&self, _:()) {
}
}
fn main() {
let mut foo = Foo{};
foo.as_mutable(foo.as_immutable(()));
}
The MIR code for that happens to have interesting lines
StorageLive(_3); // bb0[1]: scope 2 at src/main.rs:14:5: 14:8
_3 = &mut _1; // bb0[2]: scope 2 at src/main.rs:14:5: 14:8
StorageLive(_4); // bb0[3]: scope 2 at src/main.rs:14:20: 14:40
StorageLive(_5); // bb0[4]: scope 2 at src/main.rs:14:20: 14:23
_5 = &_1; // bb0[5]: scope 2 at src/main.rs:14:20: 14:23
which if I am understanding correctly implies that MIR code can have immutable borrow while having mutable borrow.
// WARNING: This output format is intended for human consumers only
// and is subject to change without notice. Knock yourself out.
fn <impl at src/main.rs:3:1: 9:2>::as_mutable(_1: &mut Foo, _2: ()) -> () {
let mut _0: (); // return place in scope 0 at src/main.rs:4:36: 4:36
bb0: {
return; // bb0[0]: scope 0 at src/main.rs:5:6: 5:6
}
}
fn main() -> () {
let mut _0: (); // return place in scope 0 at src/main.rs:11:11: 11:11
let mut _2: (); // in scope 0 at src/main.rs:14:5: 14:41
let mut _3: &mut Foo; // in scope 0 at src/main.rs:14:5: 14:8
let mut _4: (); // in scope 0 at src/main.rs:14:20: 14:40
let mut _5: &Foo; // in scope 0 at src/main.rs:14:20: 14:23
let mut _6: (); // in scope 0 at src/main.rs:14:37: 14:39
scope 1 {
let mut _1: Foo; // "foo" in scope 1 at src/main.rs:12:9: 12:16
}
scope 2 {
}
bb0: {
StorageLive(_1); // bb0[0]: scope 0 at src/main.rs:12:9: 12:16
StorageLive(_3); // bb0[1]: scope 2 at src/main.rs:14:5: 14:8
_3 = &mut _1; // bb0[2]: scope 2 at src/main.rs:14:5: 14:8
StorageLive(_4); // bb0[3]: scope 2 at src/main.rs:14:20: 14:40
StorageLive(_5); // bb0[4]: scope 2 at src/main.rs:14:20: 14:23
_5 = &_1; // bb0[5]: scope 2 at src/main.rs:14:20: 14:23
StorageLive(_6); // bb0[6]: scope 2 at src/main.rs:14:37: 14:39
_4 = const Foo::as_immutable(move _5, move _6) -> bb1; // bb0[7]: scope 2 at src/main.rs:14:20: 14:40
// ty::Const
// + ty: for<'r> fn(&'r Foo, ()) {Foo::as_immutable}
// + val: Scalar(Bits { size: 0, bits: 0 })
// mir::Constant
// + span: src/main.rs:14:24: 14:36
// + ty: for<'r> fn(&'r Foo, ()) {Foo::as_immutable}
// + literal: Const { ty: for<'r> fn(&'r Foo, ()) {Foo::as_immutable}, val: Scalar(Bits { size: 0, bits: 0 }) }
}
bb1: {
StorageDead(_6); // bb1[0]: scope 2 at src/main.rs:14:39: 14:40
StorageDead(_5); // bb1[1]: scope 2 at src/main.rs:14:39: 14:40
_2 = const Foo::as_mutable(move _3, move _4) -> bb2; // bb1[2]: scope 2 at src/main.rs:14:5: 14:41
// ty::Const
// + ty: for<'r> fn(&'r mut Foo, ()) {Foo::as_mutable}
// + val: Scalar(Bits { size: 0, bits: 0 })
// mir::Constant
// + span: src/main.rs:14:9: 14:19
// + ty: for<'r> fn(&'r mut Foo, ()) {Foo::as_mutable}
// + literal: Const { ty: for<'r> fn(&'r mut Foo, ()) {Foo::as_mutable}, val: Scalar(Bits { size: 0, bits: 0 }) }
}
bb2: {
StorageDead(_4); // bb2[0]: scope 2 at src/main.rs:14:40: 14:41
StorageDead(_3); // bb2[1]: scope 2 at src/main.rs:14:40: 14:41
StorageDead(_1); // bb2[2]: scope 0 at src/main.rs:15:1: 15:2
return; // bb2[3]: scope 0 at src/main.rs:15:2: 15:2
}
}
fn <impl at src/main.rs:3:1: 9:2>::as_immutable(_1: &Foo, _2: ()) -> () {
let mut _0: (); // return place in scope 0 at src/main.rs:7:34: 7:34
bb0: {
return; // bb0[0]: scope 0 at src/main.rs:8:6: 8:6
}
}
That's because order of evaluation evaluates foo.
before the arguments, so at this point foo
is already "locked" for use by the method call.
There are plans to relax that. Search for two-phase borrows.
For now the solution is to split it into two expressions.
1 Like
system
Closed
August 5, 2019, 11:37am
4
This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.