Multi borrows
In this page, we discuss a limitation of types with lifetimes because of combination of Rust and Dart.
A pure Rust example
Let's get started with a Rust example (full code here):
fn f(foo: &Foo) -> &Bar { .. }
fn function_that_mutably_borrow_foo(foo: &mut Foo) { .. }
let mut foo = Foo();
let bar = f(&foo);
// function_that_mutably_borrow_foo(&mut foo); // <-- cannot do it here, since bar is still borrowed
println!("{}", bar);
drop(bar);
function_that_mutably_borrow_foo(&mut foo); // <-- can do it here
Rust compiler does not allow us to call the function_that_mutably_borrow_foo
at the first place,
because bar
immutably borrows foo
and is still alive.
Recall that we are forbidden from having both immutable borrows and mutable borrows on the same object.
(Uncomment the line in Rust playground to see it in action.)
Translate to Dart
The equivalent code written in Dart is as follows.
var foo = Foo();
var bar = f(foo);
// functionThatMutablyBorrowFoo(foo); // <-- cannot do it here
bar.dispose();
functionThatMutablyBorrowFoo(foo); // <-- can do it here
As can be expected, it has the same issue, except that the form of issue will be waiting at runtime instead of errors at compile time.
In addition,
though we usually do not need to manually call dispose
(because GC will do it for us),
here we need to do it manually.
This is because we want bar
not to borrow foo
after that one,
and there is no (at least yet) deep static analysis on Dart to automatically do so.
Therefore, the takeaway is:
If we want to borrow foo
mutably,
remember to dispose
the bar
object before that.
If this makes your scenario much more complicated, feel free to open an issue to discuss your scenario, and we can discuss how to improve it. For example, we may introduce something like a "scope" where references out of scope will be disposed.
In some scenarios, the alternatives introduced in the next page may be appealing because of this.