在闭包中借用可变引用来对向量进行排序
Borrowing mutable references in closure to sort vector
虽然我理解 Rust 的内存安全方法、所有权概念,并且对一般引用没有问题,但我正在努力弄清楚 Rust 如何让我解决一个看似微不足道的问题。
此代码是一个最小示例。
一个结构:
pub struct S {
value: Option<i8>,
}
第一次计算值的getter,然后returns存储的值:
use rand::Rng;
impl S {
fn get_value(&mut self) -> i8 {
if let Some(v) = self.value {
return v;
}
let v = rand::thread_rng().gen::<i8>();
self.value = Some(v);
v
}
}
当然,get_value()
需要一个可变引用&mut self
,因为它修改了self
.
当我想根据 get_value()
的结果对 S
的引用向量进行排序时,这种可变性是我苦苦挣扎的根源。因为我想按 get_value()
排序,sort_by
使用的比较函数将需要可变引用。
我的第一次尝试:
fn main() {
let mut a = S {value: None};
let mut b = S {value: None};
let mut c = S {value: None};
let mut v = vec![&mut a, &mut b, &mut c];
v.sort_by( |a, b| a.get_value().cmp(&b.get_value()) );
}
投掷:
error[E0596]: cannot borrow `**a` as mutable, as it is behind a `&` reference
--> src/main.rs:27:20
|
27 | v.sort_by( |a, b| a.get_v().cmp(&b.get_v()) );
| - ^ `a` is a `&` reference, so the data it refers to cannot be borrowed as mutable
| |
| help: consider changing this to be a mutable reference: `&mut &mut S`
我最初的想法是,首先拥有一个可变引用向量将允许比较函数使用可变引用。但是,我的 getter 函数借用了对可变引用的可变引用,这就是错误显示 cannot borrow '**a'
.
的原因
帮助建议更改 |a,b|
使其成为 &mut &mut S
引用。
&mut &mut
和&&mut
一样吗?
这是否意味着我应该将其更改为 |mut a:&&mut S, mut b:&&mut S|
?
sort_by(<closure>)
中的闭包具有签名 FnMut(&T, &T) -> Ordering
,因为项目 在排序期间必须是不可变的 。如果可以在排序过程中改变项目,那么您将如何判断集合何时真正排序?无法验证排序的正确性!
此外,sort_by
是一种基于比较的排序,这意味着它将至少访问集合中的每个项目一次,因此 每个 项目将在整个过程中被初始化排序,所以在这种情况下使用惰性初始化 getter 并不能真正获得任何好处。
如果出于某种原因你想坚持延迟初始化 getter 那么我的建议是在排序之前初始化所有项目:
use rand::Rng;
pub struct S {
value: Option<i8>,
}
impl S {
fn get_value(&mut self) -> i8 {
if let Some(v) = self.value {
return v;
}
let v = rand::thread_rng().gen::<i8>();
self.value = Some(v);
v
}
}
fn main() {
let mut a = S { value: None };
let mut b = S { value: None };
let mut c = S { value: None };
let mut v = vec![&mut a, &mut b, &mut c];
// initialize all items, it's what would
// happen in the sort below if it allowed
// mutating items mid-sort anyway
v.iter_mut().for_each(|i| {
i.get_value();
});
// sort initialized items without mutating them
v.sort_by(|a, b| a.value.cmp(&b.value));
}
假设您的示例已简化,并且在您的实际代码中实际上需要可变性,这可能是内部可变性的一个很好的例子,例如Cell
:
use std::cell::Cell;
pub struct S {
value: Cell<Option<i8>>,
}
这意味着您可以更新 getter 方法,这样它就不需要可变的自引用:
impl S {
fn get_value(&self) -> i8 {
if let Some(v) = self.value.get() {
return v;
}
let v = rand::thread_rng().gen::<i8>();
self.value.set(Some(v));
v
}
}
您也可以删除其他一些可变引用:
fn main() {
let a = S { value: Cell::default() };
let b = S { value: Cell::default() };
let c = S { value: Cell::default() };
let mut v = vec![&a, &b, &c];
v.sort_by(|a, b| a.get_value().cmp(&b.get_value()));
}
虽然我理解 Rust 的内存安全方法、所有权概念,并且对一般引用没有问题,但我正在努力弄清楚 Rust 如何让我解决一个看似微不足道的问题。
此代码是一个最小示例。
一个结构:
pub struct S {
value: Option<i8>,
}
第一次计算值的getter,然后returns存储的值:
use rand::Rng;
impl S {
fn get_value(&mut self) -> i8 {
if let Some(v) = self.value {
return v;
}
let v = rand::thread_rng().gen::<i8>();
self.value = Some(v);
v
}
}
当然,get_value()
需要一个可变引用&mut self
,因为它修改了self
.
当我想根据 get_value()
的结果对 S
的引用向量进行排序时,这种可变性是我苦苦挣扎的根源。因为我想按 get_value()
排序,sort_by
使用的比较函数将需要可变引用。
我的第一次尝试:
fn main() {
let mut a = S {value: None};
let mut b = S {value: None};
let mut c = S {value: None};
let mut v = vec![&mut a, &mut b, &mut c];
v.sort_by( |a, b| a.get_value().cmp(&b.get_value()) );
}
投掷:
error[E0596]: cannot borrow `**a` as mutable, as it is behind a `&` reference
--> src/main.rs:27:20
|
27 | v.sort_by( |a, b| a.get_v().cmp(&b.get_v()) );
| - ^ `a` is a `&` reference, so the data it refers to cannot be borrowed as mutable
| |
| help: consider changing this to be a mutable reference: `&mut &mut S`
我最初的想法是,首先拥有一个可变引用向量将允许比较函数使用可变引用。但是,我的 getter 函数借用了对可变引用的可变引用,这就是错误显示 cannot borrow '**a'
.
帮助建议更改 |a,b|
使其成为 &mut &mut S
引用。
&mut &mut
和&&mut
一样吗?
这是否意味着我应该将其更改为 |mut a:&&mut S, mut b:&&mut S|
?
sort_by(<closure>)
中的闭包具有签名 FnMut(&T, &T) -> Ordering
,因为项目 在排序期间必须是不可变的 。如果可以在排序过程中改变项目,那么您将如何判断集合何时真正排序?无法验证排序的正确性!
此外,sort_by
是一种基于比较的排序,这意味着它将至少访问集合中的每个项目一次,因此 每个 项目将在整个过程中被初始化排序,所以在这种情况下使用惰性初始化 getter 并不能真正获得任何好处。
如果出于某种原因你想坚持延迟初始化 getter 那么我的建议是在排序之前初始化所有项目:
use rand::Rng;
pub struct S {
value: Option<i8>,
}
impl S {
fn get_value(&mut self) -> i8 {
if let Some(v) = self.value {
return v;
}
let v = rand::thread_rng().gen::<i8>();
self.value = Some(v);
v
}
}
fn main() {
let mut a = S { value: None };
let mut b = S { value: None };
let mut c = S { value: None };
let mut v = vec![&mut a, &mut b, &mut c];
// initialize all items, it's what would
// happen in the sort below if it allowed
// mutating items mid-sort anyway
v.iter_mut().for_each(|i| {
i.get_value();
});
// sort initialized items without mutating them
v.sort_by(|a, b| a.value.cmp(&b.value));
}
假设您的示例已简化,并且在您的实际代码中实际上需要可变性,这可能是内部可变性的一个很好的例子,例如Cell
:
use std::cell::Cell;
pub struct S {
value: Cell<Option<i8>>,
}
这意味着您可以更新 getter 方法,这样它就不需要可变的自引用:
impl S {
fn get_value(&self) -> i8 {
if let Some(v) = self.value.get() {
return v;
}
let v = rand::thread_rng().gen::<i8>();
self.value.set(Some(v));
v
}
}
您也可以删除其他一些可变引用:
fn main() {
let a = S { value: Cell::default() };
let b = S { value: Cell::default() };
let c = S { value: Cell::default() };
let mut v = vec![&a, &b, &c];
v.sort_by(|a, b| a.get_value().cmp(&b.get_value()));
}