How to move values ​​from an array?

I own an array of size 3, and I would like to iterate over it, moving elements when I go. Basically, I would like to have an IntoIterator for a fixed-size array.

Since arrays do not implement this feature in the standard library (I understand why), is there a workaround to get the desired effect? My objects are not Copy and Clone . It would be nice for me to create a Vec from an array and then repeat in Vec , but I don’t even know how to do it.

(For information, I would like to execute the Complete array)

Here is a simple example of a situation (with a naive attempt to 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); } } 

playground

gives

 error[E0507]: cannot move out of borrowed content --> src/main.rs:14:13 | 14 | bar(*a); | ^^ cannot move out of borrowed content 
+5
source share
2 answers

The main thing you need is some way to get the value from the array without moving it. Two such solutions:

  • Use mem::replace and mem::uninitialized to replace every value in the array with garbage, returning the original:

     let one = unsafe { mem::replace(&mut v[0], mem::uninitialized()) }; bar(one); 
  • Use ptr::read to leave the value in the array, but return it back:

     let two = unsafe { ptr::read(&v[0]) }; bar(two); 

It is just a question of one of them several times in the cycle, and you are good to go.

There is only one tiny problem: do you see those unsafe ? You guessed it; this is completely, horribly violated in the broader case:

  • We leave this array full of arbitrary bits, which will still be treated as Foo . In the case of this, nothing special happens when this array goes out of scope, since there is no special Drop implementation for the Foo type, but if that were the case, it would be access to invalid memory. Bad news!
  • If panic occurs in the middle of moving values ​​(for example, somewhere inside the bar function), the array will be in a partially uninitialized state. This is another (subtle) way in which the Drop implementation can be implemented, so now we need to know which values ​​still belong to the array and which were deleted. We are responsible for releasing the values ​​that we still own, and not others.
  • Nothing prevents us from accidentally accessing newly invalid values ​​in the array.

The correct solution is to keep track of how many of the values ​​in the array are valid / invalid. When the array is discarded, you can leave the remaining valid elements and ignore the invalid ones. It would also be very nice if we could do this job for arrays of different sizes ...

Where is arrayvec located. It does not have an exact implementation (because it is smarter), but it does have the same semantics:

 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); } } 
+8
source

You can use the Option<Foo> array instead of the Foo array. Of course, he has some penalty for memory. The take() function replaces the value in the None array.

 #[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)); } } 
+6
source

Source: https://habr.com/ru/post/1238900/


All Articles