Initialize boxed slice without clone or copy

2019-08-10 20:59发布

问题:

I'm trying to initialize a boxed slice of None values, such that the underlying type T does not need to implement Clone or Copy. Here a few ideal solutions:

fn by_vec<T>() -> Box<[Option<T>]> {
    vec![None; 5].into_boxed_slice()
}

fn by_arr<T>() -> Box<[Option<T>]> {
    Box::new([None; 5])
}

Unfortunately, the by_vec implementation requires T: Clone and the by_arr implemenation requires T: Copy. I've experimented with a few more approaches:

fn by_vec2<T>() -> Box<[Option<T>]> {
    let v = &mut Vec::with_capacity(5);
    for i in 0..v.len() {
        v[i] = None;
    }
    v.into_boxed_slice() // Doesn't work: cannot move out of borrowed content
}

fn by_iter<T>() -> Box<[Option<T>]> {
     (0..5).map(|_| None).collect::<Vec<Option<T>>>().into_boxed_slice()
}

by_vec2 doesn't get past the compiler (I'm not sure I understand why), but by_iter does. I'm concerned about the performance of collect -- will it need to resize the vector it is collecting into as it iterates, or can it allocate the correct sized vector to begin with?

Maybe I'm going about this all wrong -- I'm very new to Rust, so any tips would be appreciated!

回答1:

Let's start with by_vec2. You are taking a &mut reference to a Vec. You shouldn't do that, work directly with the Vec and make the v binding mutable.

Then you are iterating over the length of a Vec with a capacity of 5 and a length of 0. That means your loop never gets executed. What you wanted was to iterate over 0..v.cap().

Since your v is still of length 0, accessing v[i] in the loop will panic at runtime. What you actually want is v.push(None). This would normally cause reallocations, but in your case you already allocated with Vec::with_capacity, so pushing 5 times will not allocate.

This time around we did not take a reference to the Vec so into_boxed_slice will actually work.

fn by_vec2<T>() -> Box<[Option<T>]> {
    let mut v = Vec::with_capacity(5);
    for _ in 0..v.capacity() {
        v.push(None);
    }
    v.into_boxed_slice()
}

Your by_iter function actually only allocates once. The Range iterator created by 0..5 knows that is exactly 5 elements long. So collect will in fact check that length and allocate only once.



标签: rust