当借用在方法调用之后时,如何借用两个不相交的字段?
How to borrow two disjoint fields when the borrow is behind a method call?
在下面的代码中,我有一个带有只读字段 a
和一堆读写字段的结构 Foo
。当直接从结构中借用单独的字段时,借用没有问题。但是,当我在方法调用后面隐藏借用时,它说我不能再借用了。
#![allow(unused_variables)]
#![allow(unused_mut)]
#![allow(dead_code)]
struct Foo {
a: Vec<i32>, // Public read-only field.
pub b: Vec<f32>, // Public read-write field.
pub c: Vec<i32>, // Public read-write field.
// ... maybe more fields ...
pub z: Vec<bool>, // Public read-write field.
}
impl Foo {
pub fn new() -> Self {
Self {
a: vec![1, 2, 3],
b: vec![1.0, 2.0, 3.0],
c: vec![-3, 0, 3],
z: vec![false, true],
}
}
pub fn borrow_a(&self) -> &Vec<i32> {
&self.a
}
}
pub fn main() {
let mut foo = Foo::new();
{ // This is okay.
let x = &foo.a; // Immutably borrow `a`.
let mut y = &mut foo.b; // Mutably borrow `b`.
for i in x { } // Immutably use `a`.
}
{ // This creates an error.
let x = foo.borrow_a(); // Immutably borrow `a`.
let mut y = &mut foo.b; // Mutably borrow `b`.
for i in x { } // Immutably use `a`.
}
}
error[E0502]: cannot borrow `foo.b` as mutable because it is also borrowed as immutable
--> src/main.rs:39:21
|
38 | let x = foo.borrow_a(); // Immutably borrow `a`.
| --- immutable borrow occurs here
39 | let mut y = &mut foo.b; // Mutably borrow `b`.
| ^^^^^^^^^^ mutable borrow occurs here
40 | for i in x { } // Immutably use `a`.
| - immutable borrow later used here
有什么方法可以告诉编译器代码没问题,我借用了两个不相交的字段?或者有其他符合人体工程学的解决方案吗?
可以使用的不同技术
使用Splitting Borrow
建议使用 Splitting Borrow 来借用字段。这将如下例所示工作。
但是,对于维护者的用户来说,这不符合人体工程学 API。如果他们借用了 foo
中的字段,现在还想借用 a
,他们将不得不重写他们的借用以通过 Split Borrow 方法。他们还必须匹配他们想要借用的字段。由于它们与元组匹配,因此并不完全清楚它们与哪些字段匹配。
此外,在 Foo
中引入新的 public 字段会破坏一切,因为 split_borrow
的签名必须更改。
总而言之,当字段数量较少时,这可以工作。
#![allow(unused_variables)]
#![allow(unused_mut)]
#![allow(dead_code)]
struct Foo {
a: Vec<i32>, // Public read-only field.
pub b: Vec<f32>, // Public read-write field.
pub c: Vec<i32>, // Public read-write field.
// ... maybe more fields ...
pub z: Vec<bool>, // Public read-write field.
}
impl Foo {
pub fn new() -> Self {
Self {
a: vec![1, 2, 3],
b: vec![1.0, 2.0, 3.0],
c: vec![-3, 0, 3],
z: vec![false, true],
}
}
pub fn split_borrow(&mut self) -> (&Vec<i32>, &mut Vec<f32>, &mut Vec<i32>, &mut Vec<bool>) {
(&self.a, &mut self.b, &mut self.c, &mut self.z)
}
}
pub fn main() {
let mut foo = Foo::new();
{ // This is okay.
let (a, ref mut b, ..) = foo.split_borrow();
for i in a { }
}
{ // This is okay.
let (a, _, _, ref mut z) = foo.split_borrow();
for i in a { }
}
{ // This is okay if we re-borrow the values
// between each use.
let (a, ref mut b, ..) = foo.split_borrow();
b.push(4.0);
let (a, _, _, ref mut z) = foo.split_borrow();
// Can't use b from this point.
z.push(false);
println!("{:?}, {:?}", a, z);
}
{ // It's not okay to mix-and-match variables
// from different borrows, as they're exclusively
// bound to `foo`.
let (a, ref mut b, ..) = foo.split_borrow();
let (_, _, _, ref mut z) = foo.split_borrow();
for i in a { }
}
}
使用Interior mutability
展示了如何通过将类型包装在 std::cell::Cell
中来模拟在字段中使用 mut
的旧结构。如果我们将所有可变字段包装在 Cell
中并且仅对 Foo
.
的不可变借用进行操作,这可能是一个解决方案
但是,这将数据限制为单线程,因为 std::cell::Cell
实现了 !Sync
. It also restrict the data to only be Copy
。此外,这确实允许可变字段在我们传递不可变引用的代码位置发生变化,因此期望它们不会发生变化。我不认为这是一个解决方案,但可以工作。
包装成只读类型
This answer 展示了如何将只读值包装到不可变结构中。这是迄今为止最干净、最符合人体工程学的解决方案,如下例所示。由于现在所有字段都是 public,借用检查器能够确定我们实际上是在借用不相交的字段。
唯一不方便的是需要在每个模块中定义ReadOnly
结构。这是因为您希望 get_mut
只能由拥有 ReadOnly
的结构访问(换句话说,get_mut
不能是 public)。
#![allow(unused_variables)]
#![allow(unused_mut)]
#![allow(dead_code)]
use std::ops::Deref;
struct ReadOnly<T> {
data: T,
}
impl<T> ReadOnly<T> {
pub fn new(data: T) -> Self {
ReadOnly { data }
}
pub fn get(&self) -> &T {
&self.data
}
// Private function for mutating the
// data from within Foo itself.
fn get_mut(&mut self) -> &mut T {
&mut self.data
}
}
impl<T> Deref for ReadOnly<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.data
}
}
struct Foo {
pub a: ReadOnly<Vec<i32>>, // Public read-only field.
pub b: Vec<f32>, // Public read-write field.
pub c: Vec<i32>, // Public read-write field.
// ... maybe more fields ...
pub z: Vec<bool>, // Public read-write field.
}
impl Foo {
pub fn new() -> Self {
Self {
a: ReadOnly::new(vec![1, 2, 3]),
b: vec![1.0, 2.0, 3.0],
c: vec![-3, 0, 3],
z: vec![false, true],
}
}
}
pub fn main() {
let mut foo = Foo::new();
{ // This now works.
let x = foo.a.get(); // Immutably borrow `a`.
let mut y = &mut foo.b; // Mutably borrow `b`.
for i in x { } // Immutably use `a`.
}
{ // This is now erroneous.
let mut x = &mut foo.a; // Can still borrow ReadOnly as mutable.
let mut y = &mut foo.b; // Mutably borrow `b`.
for i in x.iter_mut() { } // Can't use `a` as mutable.
}
}
TL;DR
单个字段的只读访问器或“获取器”很容易破坏有效借用。相反,字段应该包装在 ReadOnly 结构中,或者如果字段数量较少,则应提供 Split Borrow 方法。
在下面的代码中,我有一个带有只读字段 a
和一堆读写字段的结构 Foo
。当直接从结构中借用单独的字段时,借用没有问题。但是,当我在方法调用后面隐藏借用时,它说我不能再借用了。
#![allow(unused_variables)]
#![allow(unused_mut)]
#![allow(dead_code)]
struct Foo {
a: Vec<i32>, // Public read-only field.
pub b: Vec<f32>, // Public read-write field.
pub c: Vec<i32>, // Public read-write field.
// ... maybe more fields ...
pub z: Vec<bool>, // Public read-write field.
}
impl Foo {
pub fn new() -> Self {
Self {
a: vec![1, 2, 3],
b: vec![1.0, 2.0, 3.0],
c: vec![-3, 0, 3],
z: vec![false, true],
}
}
pub fn borrow_a(&self) -> &Vec<i32> {
&self.a
}
}
pub fn main() {
let mut foo = Foo::new();
{ // This is okay.
let x = &foo.a; // Immutably borrow `a`.
let mut y = &mut foo.b; // Mutably borrow `b`.
for i in x { } // Immutably use `a`.
}
{ // This creates an error.
let x = foo.borrow_a(); // Immutably borrow `a`.
let mut y = &mut foo.b; // Mutably borrow `b`.
for i in x { } // Immutably use `a`.
}
}
error[E0502]: cannot borrow `foo.b` as mutable because it is also borrowed as immutable
--> src/main.rs:39:21
|
38 | let x = foo.borrow_a(); // Immutably borrow `a`.
| --- immutable borrow occurs here
39 | let mut y = &mut foo.b; // Mutably borrow `b`.
| ^^^^^^^^^^ mutable borrow occurs here
40 | for i in x { } // Immutably use `a`.
| - immutable borrow later used here
有什么方法可以告诉编译器代码没问题,我借用了两个不相交的字段?或者有其他符合人体工程学的解决方案吗?
可以使用的不同技术
使用Splitting Borrow
但是,对于维护者的用户来说,这不符合人体工程学 API。如果他们借用了 foo
中的字段,现在还想借用 a
,他们将不得不重写他们的借用以通过 Split Borrow 方法。他们还必须匹配他们想要借用的字段。由于它们与元组匹配,因此并不完全清楚它们与哪些字段匹配。
此外,在 Foo
中引入新的 public 字段会破坏一切,因为 split_borrow
的签名必须更改。
总而言之,当字段数量较少时,这可以工作。
#![allow(unused_variables)]
#![allow(unused_mut)]
#![allow(dead_code)]
struct Foo {
a: Vec<i32>, // Public read-only field.
pub b: Vec<f32>, // Public read-write field.
pub c: Vec<i32>, // Public read-write field.
// ... maybe more fields ...
pub z: Vec<bool>, // Public read-write field.
}
impl Foo {
pub fn new() -> Self {
Self {
a: vec![1, 2, 3],
b: vec![1.0, 2.0, 3.0],
c: vec![-3, 0, 3],
z: vec![false, true],
}
}
pub fn split_borrow(&mut self) -> (&Vec<i32>, &mut Vec<f32>, &mut Vec<i32>, &mut Vec<bool>) {
(&self.a, &mut self.b, &mut self.c, &mut self.z)
}
}
pub fn main() {
let mut foo = Foo::new();
{ // This is okay.
let (a, ref mut b, ..) = foo.split_borrow();
for i in a { }
}
{ // This is okay.
let (a, _, _, ref mut z) = foo.split_borrow();
for i in a { }
}
{ // This is okay if we re-borrow the values
// between each use.
let (a, ref mut b, ..) = foo.split_borrow();
b.push(4.0);
let (a, _, _, ref mut z) = foo.split_borrow();
// Can't use b from this point.
z.push(false);
println!("{:?}, {:?}", a, z);
}
{ // It's not okay to mix-and-match variables
// from different borrows, as they're exclusively
// bound to `foo`.
let (a, ref mut b, ..) = foo.split_borrow();
let (_, _, _, ref mut z) = foo.split_borrow();
for i in a { }
}
}
使用Interior mutability
std::cell::Cell
中来模拟在字段中使用 mut
的旧结构。如果我们将所有可变字段包装在 Cell
中并且仅对 Foo
.
但是,这将数据限制为单线程,因为 std::cell::Cell
实现了 !Sync
. It also restrict the data to only be Copy
。此外,这确实允许可变字段在我们传递不可变引用的代码位置发生变化,因此期望它们不会发生变化。我不认为这是一个解决方案,但可以工作。
包装成只读类型
This answer 展示了如何将只读值包装到不可变结构中。这是迄今为止最干净、最符合人体工程学的解决方案,如下例所示。由于现在所有字段都是 public,借用检查器能够确定我们实际上是在借用不相交的字段。
唯一不方便的是需要在每个模块中定义ReadOnly
结构。这是因为您希望 get_mut
只能由拥有 ReadOnly
的结构访问(换句话说,get_mut
不能是 public)。
#![allow(unused_variables)]
#![allow(unused_mut)]
#![allow(dead_code)]
use std::ops::Deref;
struct ReadOnly<T> {
data: T,
}
impl<T> ReadOnly<T> {
pub fn new(data: T) -> Self {
ReadOnly { data }
}
pub fn get(&self) -> &T {
&self.data
}
// Private function for mutating the
// data from within Foo itself.
fn get_mut(&mut self) -> &mut T {
&mut self.data
}
}
impl<T> Deref for ReadOnly<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.data
}
}
struct Foo {
pub a: ReadOnly<Vec<i32>>, // Public read-only field.
pub b: Vec<f32>, // Public read-write field.
pub c: Vec<i32>, // Public read-write field.
// ... maybe more fields ...
pub z: Vec<bool>, // Public read-write field.
}
impl Foo {
pub fn new() -> Self {
Self {
a: ReadOnly::new(vec![1, 2, 3]),
b: vec![1.0, 2.0, 3.0],
c: vec![-3, 0, 3],
z: vec![false, true],
}
}
}
pub fn main() {
let mut foo = Foo::new();
{ // This now works.
let x = foo.a.get(); // Immutably borrow `a`.
let mut y = &mut foo.b; // Mutably borrow `b`.
for i in x { } // Immutably use `a`.
}
{ // This is now erroneous.
let mut x = &mut foo.a; // Can still borrow ReadOnly as mutable.
let mut y = &mut foo.b; // Mutably borrow `b`.
for i in x.iter_mut() { } // Can't use `a` as mutable.
}
}
TL;DR
单个字段的只读访问器或“获取器”很容易破坏有效借用。相反,字段应该包装在 ReadOnly 结构中,或者如果字段数量较少,则应提供 Split Borrow 方法。