Rust 1.51
You can use std::array::IntoIter
to get a by-value array iterator:
use std::array::IntoIter;
struct Foo;
fn bar(_: Foo) {}
fn main() {
let v: [Foo; 3] = [Foo, Foo, Foo];
for a in IntoIter::new(v) {
bar(a);
}
}
Previous Rust versions
The core thing you would need is some way of getting the value out of the array without moving it.
This can be done using mem::transmute
to convert the array to an array of mem::MaybeUninit
, then using ptr::read
to leave the value in the array but get an owned value back:
let one = unsafe {
let v = mem::transmute::<_, [MaybeUninit<Foo>; 3]>(v);
ptr::read(&v[0]).assume_init()
};
bar(one);
It's just a matter of doing this a few times in a loop and you are good to go.
There's just one tiny problem: you see that unsafe
? You guessed it; this is totally, horribly broken in the wider case:
MaybeUninit
does nothing when it is dropped; this can lead to memory leaks.
- If a panic happens in the middle of moving the values out (such as somewhere within the
bar
function), the array will be in a partially-uninitialized state. This is another (subtle) path where the MaybeUninit
can be dropped, so now we have to know which values the array still owns and which have been moved out. We are responsible for freeing the values we still own and not the others.
- Nothing prevents us from accidentally accessing the newly-invalidated values in the array ourselves.
The right solution is to track how many of the values in the array are valid / invalid. When the array is dropped, you can drop the remaining valid items and ignore the invalid ones. It'd also be really nice if we could make this work for arrays of different sizes...
Which is where arrayvec comes in. It doesn't have the exact same implementation (because it's smarter), but it does have the same semantics:
use arrayvec::ArrayVec; // 0.5.2
#[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);
}
}
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…