如何获取对 Vec 中元素的多个可变引用?
How to get multiple mutable references to elements in a Vec?
我有一个很大的嵌套数据结构,想挑出一些部分传递给处理。最终,我想将部分发送到多个线程进行更新,但我想深入了解我在下面说明的简单示例。在 C 中,我只是 assemble 一个相关指针的数组。这在 Rust 中似乎可行,因为内部向量永远不需要多个可变引用。这是示例代码。
fn main() {
let mut data = Data::new(vec![2, 3, 4]);
// this works
let slice = data.get_mut_slice(1);
slice[2] = 5.0;
println!("{:?}", data);
// what I would like to do
// let slices = data.get_mut_slices(vec![0, 1]);
// slices[0][0] = 2.0;
// slices[1][0] = 3.0;
// println!("{:?}", data);
}
#[derive(Debug)]
struct Data {
data: Vec<Vec<f64>>,
}
impl Data {
fn new(lengths: Vec<usize>) -> Data {
Data {
data: lengths.iter().map(|n| vec![0_f64; *n]).collect(),
}
}
fn get_mut_slice(&mut self, index: usize) -> &mut [f64] {
&mut self.data[index][..]
}
// doesnt work
// fn get_mut_slices(&mut self, indexes: Vec<usize>) -> Vec<&mut [f64]> {
// indexes.iter().map(|i| self.get_mut_slice(*i)).collect()
// }
}
只要您非常小心,使用安全的 Rust 是可能的。诀窍是利用 Vec
上安全 .iter_mut()
和 .nth()
方法背后的标准库中的不安全 Rust 代码。这是一个工作示例,其中包含在上下文中解释代码的注释:
fn main() {
let mut data = Data::new(vec![2, 3, 4]);
// this works
let slice = data.get_mut_slice(1);
slice[2] = 5.0;
println!("{:?}", data);
// and now this works too!
let mut slices = data.get_mut_slices(vec![0, 1]);
slices[0][0] = 2.0;
slices[1][0] = 3.0;
println!("{:?}", data);
}
#[derive(Debug)]
struct Data {
data: Vec<Vec<f64>>,
}
impl Data {
fn new(lengths: Vec<usize>) -> Data {
Data {
data: lengths.iter().map(|n| vec![0_f64; *n]).collect(),
}
}
fn get_mut_slice(&mut self, index: usize) -> &mut [f64] {
&mut self.data[index][..]
}
// now works!
fn get_mut_slices(&mut self, mut indexes: Vec<usize>) -> Vec<&mut [f64]> {
// sort indexes for easier processing
indexes.sort();
let index_len = indexes.len();
// early return for edge case
if index_len == 0 {
return Vec::new();
}
// check that the largest index is in bounds
let max_index = indexes[index_len - 1];
if max_index > self.data.len() {
panic!("{} index is out of bounds of data", max_index);
}
// check that we have no overlapping indexes
indexes.dedup();
let uniq_index_len = indexes.len();
if index_len != uniq_index_len {
panic!("cannot return aliased mut refs to overlapping indexes");
}
// leverage the unsafe code that's written in the standard library
// to safely get multiple unique disjoint mutable references
// out of the Vec
let mut mut_slices_iter = self.data.iter_mut();
let mut mut_slices = Vec::with_capacity(index_len);
let mut last_index = 0;
for curr_index in indexes {
mut_slices.push(
mut_slices_iter
.nth(curr_index - last_index)
.unwrap()
.as_mut_slice(),
);
last_index = curr_index;
}
// return results
mut_slices
}
}
What I believe I learned is that the Rust compiler demands an iterator in this situation because that is the only way it can know that each mut slice comes from a different vector.
编译器实际上并不知道。它只知道迭代器 returns mut 引用。底层实现使用不安全的 Rust,但方法 iter_mut()
本身是安全的,因为实现保证只发出每个 mut ref 一次,并且所有 mut ref 都是唯一的。
Would the compiler complain if another mut_slices_iter
was created in the for loop (which could grab the same data twice)?
是的。在 Vec
上调用 iter_mut()
可变地借用它并且重叠可变借用相同的数据违反了 Rust 的所有权规则,所以你不能在同一范围内调用 iter_mut()
两次(除非迭代器返回由第一个调用在第二个调用之前挂断)。
Also am I right that the .nth
method will call next()
n times so it is ultimately theta(n) on the first axis?
不完全是。这是 nth
的默认实现但是通过在 Vec
上调用 iter_mut()
返回的迭代器使用 its own custom implementation 并且它似乎跳过迭代器中过去的项目而不调用 next()
所以它应该和你只是定期索引到 Vec
一样快,即使用 .nth()
获得 3 个随机索引的项目在 10000 个项目的迭代器上和在迭代器上一样快10 个项目,尽管这仅适用于从支持快速随机访问的集合创建的迭代器,如 Vec
s.
如果你想要唯一索引,这实际上更有意义,因为你不能't/shoudn对同一元素有两个可变引用。您可以使用 HashSet
而不是 Vec
并使用一些迭代器组合:
fn get_mut_slices(&mut self, indexes: HashSet<usize>) -> Vec<&mut [f64]> {
self.data
.iter_mut()
.enumerate()
.filter(|(i, _)| indexes.contains(i))
.map(|(_, e)| e.as_mut_slice())
.collect()
}
您仍然可以使用 Vec
作为此选项,但使用 contains
时效率会低得多:
fn get_mut_slices(&mut self, indexes: Vec<usize>) -> Vec<&mut [f64]> {
self.data
.iter_mut()
.enumerate()
.filter(|(i, _)| indexes.contains(i))
.map(|(_, e)| e.as_mut_slice())
.collect()
}
我有一个很大的嵌套数据结构,想挑出一些部分传递给处理。最终,我想将部分发送到多个线程进行更新,但我想深入了解我在下面说明的简单示例。在 C 中,我只是 assemble 一个相关指针的数组。这在 Rust 中似乎可行,因为内部向量永远不需要多个可变引用。这是示例代码。
fn main() {
let mut data = Data::new(vec![2, 3, 4]);
// this works
let slice = data.get_mut_slice(1);
slice[2] = 5.0;
println!("{:?}", data);
// what I would like to do
// let slices = data.get_mut_slices(vec![0, 1]);
// slices[0][0] = 2.0;
// slices[1][0] = 3.0;
// println!("{:?}", data);
}
#[derive(Debug)]
struct Data {
data: Vec<Vec<f64>>,
}
impl Data {
fn new(lengths: Vec<usize>) -> Data {
Data {
data: lengths.iter().map(|n| vec![0_f64; *n]).collect(),
}
}
fn get_mut_slice(&mut self, index: usize) -> &mut [f64] {
&mut self.data[index][..]
}
// doesnt work
// fn get_mut_slices(&mut self, indexes: Vec<usize>) -> Vec<&mut [f64]> {
// indexes.iter().map(|i| self.get_mut_slice(*i)).collect()
// }
}
只要您非常小心,使用安全的 Rust 是可能的。诀窍是利用 Vec
上安全 .iter_mut()
和 .nth()
方法背后的标准库中的不安全 Rust 代码。这是一个工作示例,其中包含在上下文中解释代码的注释:
fn main() {
let mut data = Data::new(vec![2, 3, 4]);
// this works
let slice = data.get_mut_slice(1);
slice[2] = 5.0;
println!("{:?}", data);
// and now this works too!
let mut slices = data.get_mut_slices(vec![0, 1]);
slices[0][0] = 2.0;
slices[1][0] = 3.0;
println!("{:?}", data);
}
#[derive(Debug)]
struct Data {
data: Vec<Vec<f64>>,
}
impl Data {
fn new(lengths: Vec<usize>) -> Data {
Data {
data: lengths.iter().map(|n| vec![0_f64; *n]).collect(),
}
}
fn get_mut_slice(&mut self, index: usize) -> &mut [f64] {
&mut self.data[index][..]
}
// now works!
fn get_mut_slices(&mut self, mut indexes: Vec<usize>) -> Vec<&mut [f64]> {
// sort indexes for easier processing
indexes.sort();
let index_len = indexes.len();
// early return for edge case
if index_len == 0 {
return Vec::new();
}
// check that the largest index is in bounds
let max_index = indexes[index_len - 1];
if max_index > self.data.len() {
panic!("{} index is out of bounds of data", max_index);
}
// check that we have no overlapping indexes
indexes.dedup();
let uniq_index_len = indexes.len();
if index_len != uniq_index_len {
panic!("cannot return aliased mut refs to overlapping indexes");
}
// leverage the unsafe code that's written in the standard library
// to safely get multiple unique disjoint mutable references
// out of the Vec
let mut mut_slices_iter = self.data.iter_mut();
let mut mut_slices = Vec::with_capacity(index_len);
let mut last_index = 0;
for curr_index in indexes {
mut_slices.push(
mut_slices_iter
.nth(curr_index - last_index)
.unwrap()
.as_mut_slice(),
);
last_index = curr_index;
}
// return results
mut_slices
}
}
What I believe I learned is that the Rust compiler demands an iterator in this situation because that is the only way it can know that each mut slice comes from a different vector.
编译器实际上并不知道。它只知道迭代器 returns mut 引用。底层实现使用不安全的 Rust,但方法 iter_mut()
本身是安全的,因为实现保证只发出每个 mut ref 一次,并且所有 mut ref 都是唯一的。
Would the compiler complain if another
mut_slices_iter
was created in the for loop (which could grab the same data twice)?
是的。在 Vec
上调用 iter_mut()
可变地借用它并且重叠可变借用相同的数据违反了 Rust 的所有权规则,所以你不能在同一范围内调用 iter_mut()
两次(除非迭代器返回由第一个调用在第二个调用之前挂断)。
Also am I right that the
.nth
method will callnext()
n times so it is ultimately theta(n) on the first axis?
不完全是。这是 nth
的默认实现但是通过在 Vec
上调用 iter_mut()
返回的迭代器使用 its own custom implementation 并且它似乎跳过迭代器中过去的项目而不调用 next()
所以它应该和你只是定期索引到 Vec
一样快,即使用 .nth()
获得 3 个随机索引的项目在 10000 个项目的迭代器上和在迭代器上一样快10 个项目,尽管这仅适用于从支持快速随机访问的集合创建的迭代器,如 Vec
s.
如果你想要唯一索引,这实际上更有意义,因为你不能't/shoudn对同一元素有两个可变引用。您可以使用 HashSet
而不是 Vec
并使用一些迭代器组合:
fn get_mut_slices(&mut self, indexes: HashSet<usize>) -> Vec<&mut [f64]> {
self.data
.iter_mut()
.enumerate()
.filter(|(i, _)| indexes.contains(i))
.map(|(_, e)| e.as_mut_slice())
.collect()
}
您仍然可以使用 Vec
作为此选项,但使用 contains
时效率会低得多:
fn get_mut_slices(&mut self, indexes: Vec<usize>) -> Vec<&mut [f64]> {
self.data
.iter_mut()
.enumerate()
.filter(|(i, _)| indexes.contains(i))
.map(|(_, e)| e.as_mut_slice())
.collect()
}