Rust 当借用在方法调用后面时,如何借用两个不相交的字段?
在下面的代码中,我有一个结构Rust 当借用在方法调用后面时,如何借用两个不相交的字段?,rust,borrow-checker,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<
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`.
}
}
有没有办法告诉编译器代码很好,我借用了两个不相交的字段?或者有其他符合人体工程学的解决方案吗?可以使用不同的技术
使用
建议使用“拆分借用”来借用字段。这将如下面的示例所示
然而,对于维护人员而言,这不是一个符合人体工程学的API。如果他们借用了foo
中的字段,并且现在还想借用a
,那么他们必须重写借用以通过拆分借用方法。他们还必须与他们想借的土地相匹配。因为它们与元组匹配,所以不完全清楚它们与哪些字段匹配
此外,在Foo
中引入一个新的公共字段将打破一切,因为split\u-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 { }
}
}
TL;博士
单个字段的只读访问器或“getter”很容易破坏有效的借用。相反,应将字段包装在只读结构中,或者如果字段数量较少,则应提供拆分借用方法。@Stargateur解决方案习惯用法在rust中似乎很常见,它有一个确定的名称吗?这难道不是因为编译器没有在函数内部查看它们借用了结构的哪些部分,所以它无法验证在
main
中借用b
是否可以吗?@MichaelAnderson可能是最接近的一个。@JonasBerlin当然,但同时,向类型系统添加借用拆分通常会泄漏实现细节和脆弱的API,因此这不是一个无害的更改。
#![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 { }
}
}
#![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.
}
}