Как переместить значения из массива?
у меня есть владение массивом размера 3, и я хотел бы повторить его, перемещая элементы по мере продвижения. В принципе, я хотел бы иметь IntoIterator
реализовано для массива фиксированного размера.
поскольку массивы не реализуют эту особенность в стандартной библиотеке (я понимаю, почему), есть ли обходной путь, чтобы получить желаемый эффект? Мои объекты не Copy
, ни Clone
. Я был бы в порядке, создав Vec
из массива, а затем повторяя в Vec
, но я даже не уверен как это сделать.
(для информации, я хотел бы исполнить массив Complete
)
вот простой пример ситуации (с наивным iter()
попытка):
// No-copy, No-clone struct
#[derive(Debug)]
struct Foo;
// A method that needs an owned Foo
fn bar(foo: Foo) {
println!("{:?}", foo);
}
fn main() {
let v: [Foo; 3] = [Foo, Foo, Foo];
for a in v.iter() {
bar(*a);
}
}
дает
error[E0507]: cannot move out of borrowed content
--> src/main.rs:14:13
|
14 | bar(*a);
| ^^ cannot move out of borrowed content
3 ответов
основная вещь, которая вам понадобится, - это способ получить значение из массива, не перемещая его. Два таких решения:
-
использовать
mem::replace
иmem::uninitialized
чтобы заменить каждое значение в массиве мусором, возвращая исходное:let one = unsafe { mem::replace(&mut v[0], mem::uninitialized()) }; bar(one);
-
использовать
ptr::read
оставить значение в массиве, но получить собственное значение назад:let two = unsafe { ptr::read(&v[0]) }; bar(two);
это всего лишь вопрос одного из них несколько раз в цикле, и вы хорошо идти.
есть только одна маленькая проблема: вы видите эти unsafe
? Вы догадались; это полностью, ужасно сломано в более широком случае:
- мы оставляем этот массив, полный произвольных битов, которые все равно будут рассматриваться как
Foo
. В этой case, ничего особенного не происходит, когда этот массив выходит за рамки там нет специальногоDrop
реализация для типаFoo
, но если бы это было, это был бы доступ к недопустимой памяти. Плохие новости! - вы мог бы использовать
mem::forget
игнорировать массив и предотвратить его удаление, но... - если паника происходит в процессе перемещения значений (например, где-то внутри
bar
function), массив будет находиться в частично неинициализированном состоянии. Это другой (тонкий) путь, гдеDrop
реализация может быть вызвана, поэтому теперь мы должны знать, какие значения все еще принадлежат массиву и которые были перемещены. Мы несем ответственность за освобождение ценностей, которыми мы все еще владеем, а не других. - ничто не мешает нам случайно получить доступ к недавно недействительным значениям в массиве самостоятельно.
правильное решение -трек сколько значений в массиве действительных / недействительных. Когда массив удаляется, вы можете удалить остальные допустимые элементы и игнорировать недопустимые. Вы также должны избегать автоматического деструктора от бега. Было бы также очень хорошо, если бы мы могли сделать эту работу для массивов разных размеров...
где arrayvec приходит. У него нет точно та же реализация (потому что она умнее), но она имеет ту же семантику:
extern crate arrayvec;
use arrayvec::ArrayVec;
#[derive(Debug)]
struct Foo;
fn bar(foo: Foo) {
println!("{:?}", foo)
}
fn main() {
let v = ArrayVec::from([Foo, Foo, Foo]);
for f in v {
bar(f);
}
}
вы можете использовать массив Option<Foo>
вместо массива Foo
. Конечно, у него есть какой-то штраф памяти. Функция take()
заменяет значение в массиве на None
.
#[derive(Debug)]
struct Foo;
// A method that needs an owned Foo
fn bar(foo: Foo) { println!("{:?}", foo); }
fn main() {
let mut v = [Some(Foo),Some(Foo),Some(Foo)];
for a in &mut v {
a.take().map(|x| bar(x));
}
}
С помощью не-лексико жизни функции (только в еженощно) и шаблон среза фиксированной длины вы можете выйти из массив:
#![feature(nll)]
#[derive(Debug)]
struct Foo;
fn bar(foo: Foo) {
println!("{:?}", foo);
}
fn main() {
let v: [Foo; 3] = [Foo, Foo, Foo];
let [a, b, c] = v;
bar(a);
bar(b);
bar(c);
}
однако это решение не масштабируется хорошо, если массив большой.
альтернативой, если вы не возражаете против дополнительного выделения, является коробка массива и преобразование его в Vec
:
fn main() {
let v: [Foo; 3] = [Foo, Foo, Foo];
let v = Vec::from(Box::new(v) as Box<[_]>);
for a in v {
bar(a);
}
}
Если массив очень большой, это может быть проблемой. Но тогда, если массив очень большой, вы не должны создавать его в стек в первую очередь!