Это возможно с использованием безопасного Rust, если вы очень осторожны. Уловка состоит в том, чтобы использовать небезопасный код Rust в стандартной библиотеке за безопасными .iter_mut()
и .nth()
методами на Vec
. Вот рабочий пример с комментариями, объясняющими код в контексте:
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
}
}
детская площадка
Я считаю, что я узнал, что компилятор Rust требует итератора в этой ситуации, потому что это единственный способ узнать, что каждый фрагмент mut происходит из другого вектора.
На самом деле компилятор этого не знает. Все, что ему известно, это то, что итератор возвращает ссылки на mut. Базовая реализация использует небезопасный Rust, но сам метод iter_mut()
безопасен, потому что реализация гарантирует, что каждый mut ref будет генерироваться только один раз и что все mut ref уникальны.
Будет ли компилятор жаловаться, если в цикле for будет создан другой mut_slices_iter
(который может получить одни и те же данные дважды)?
да. Вызов iter_mut()
на Vec
взаимно заимствует его, а перекрывающиеся изменяемые заимствования одних и тех же данных противоречат правилам владения Rust, поэтому вы не можете вызвать iter_mut()
дважды в одной и той же области (если итератор, возвращенный первым вызовом, не будет отброшен перед вторым вызовом).
Также я прав в том, что метод .nth
будет вызывать next()
n раз, так что в конечном итоге это theta (n) на первой оси?
Не совсем. Это реализация по умолчанию для nth
, НО итератор, возвращаемый вызовом iter_mut()
на Vec
, использует собственная индивидуальная реализация, и кажется, что он пропускает предыдущие элементы в итераторе без вызова next()
, поэтому он должен быть таким же быстрым, как если бы вы просто регулярно индексировали в Vec
, т.е. получение 3 случайно проиндексированных элементов с использованием .nth()
будет таким же быстрым на итераторе из 10000 элементов, как и на итераторе из 10 элементов, хотя это особенно верно только для итераторов, созданных из коллекций, которые поддерживают быстрый произвольный доступ, например Vec
s.
person
pretzelhammer
schedule
14.02.2021
data.get_mut_slices(vec![0, 0])
было бы возможно неопределенное поведение, возвращающее две изменяемые ссылки на одни и те же элементы. - person kmdreko   schedule 14.02.2021