Ted*_*man 9 rust borrow-checker
在下面的代码中,我有一个Foo带有只读字段a和一堆读写字段的结构。当直接从结构中借用单独的字段时,没有借用问题。但是,当我将借用隐藏在方法调用后面时,它表示我不再可以借用。
#![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`.
}
}
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
有什么方法可以告诉编译器代码很好并且我借用了两个不相交的字段?或者还有其他符合人体工程学的解决方案吗?
此评论建议使用 Splitting Borrow 来借用字段。这将如下例所示工作。
然而,对于用户或维护者来说,这并不是一个符合人体工程学的 API。如果他们已经借用了 中的字段foo并且现在还想借用a,则他们必须重写借用以通过 Split Borrow 方法。他们还必须与他们想要借用的字段进行匹配。由于它们与元组匹配,因此并不完全清楚它们与哪些字段匹配。
此外,引入一个新的公共字段Foo会破坏一切,因为 的签名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 { }
}
}
Run Code Online (Sandbox Code Playgroud)
这个答案展示了如何通过mut将类型包装在std::cell::Cell. 如果我们将所有可变字段包装在 a 中Cell并且仅对 的不可变借用进行操作,这可能是一个解决方案Foo。
但是,这将数据限制为单线程,如std::cell::CellImplements !Sync。它还限制数据只能是Copy. 此外,这确实允许可变字段在我们传递不可变引用的代码位置发生变化,因此期望它们不会发生变化。我不认为这是一个解决方案,但可以工作。
这个答案展示了如何将只读值包装到不可变的结构中。这是迄今为止最干净、最符合人体工程学的解决方案,如下例所示。由于所有字段现在都是公共的,借用检查器能够发现我们实际上借用了不相交的字段。
唯一的不便在于你需要ReadOnly在每个模块中定义结构。这是因为您希望get_mut只能由拥有的结构访问ReadOnly(换句话说,get_mut不能公开)。
#![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.
}
}
Run Code Online (Sandbox Code Playgroud)
各个字段的只读访问器或“getters”很容易破坏有效的借用。相反,字段应该包装在 ReadOnly 结构中,或者如果字段数量较少,则应提供 Split Borrow 方法。
| 归档时间: |
|
| 查看次数: |
1950 次 |
| 最近记录: |