How to initialize Vec<Option<T>> with None

Hi,

I get this error message

error[E0277]: the trait bound `services::pool::zmq::Socket: std::clone::Clone` is not satisfied
   --> src/services/pool/networker.rs:193:49
    |
193 |         let mut sockets: Vec<Option<ZSocket>> = vec![None; nodes.len()];
    |                                                 ^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::clone::Clone` is not implemented for `services::pool::zmq::Socket`
    |
    = note: required because of the requirements on the impl of `std::clone::Clone` for `std::option::Option<services::pool::zmq::Socket>`
    = note: required by `std::vec::from_elem`

and imaging that None should work because None is, kind of, the same for all types.

What is the best (for some definition of best) way to initialize such a vector with all None values?

Thanks
Axel

I'd go with:

let mut sockets: Vec<Option<ZSocket>> =
    (0..nodes.len()).map(|_| None).collect();

or

let mut sockets: Vec<Option<ZSocket>> = Vec::new();
sockets.resize_with(nodes.len(), || None);

Edit: There's also repeat_with added with 1.28, that's perhaps clearer than mapping the range:

std::iter::repeat_with(|| None).take(nodes.len()).collect()
3 Likes

I found a good discussion here Methods for Array Initialization in Rust | Josh Mcguigan - The things I write
The limitation 32 seems especially absurd.
Still think that the case of None should be easier than all this.

Array initialization ([expr; count]) is a compiler builtin, on the other hand, vector initialization (vec![expr; count]) is fully a library construct. So please note we're talking about two separate constructs (although they share similar syntax for a reason).

vec![expr; count] must choose whether to:

  1. evaluate expr once and then Clone it (this is how it's implemented), or
  2. evaluate the expression count times. This doesn't require any bounds, and would work with None case.

Unfortunately, a macro has no power to distinguish whether expr has side effects and cannot support both 1. and 2. So if we don't want the user to be surprised that the expr is evaluated multiple times, we're stuck with 1.

Honestly, I'm not a fan of the vec macro whatsoever. I'd much prefer an API like below:

impl<T> Vec<T> {
    fn repeated_default(count: usize) where T: Default -> Vec<T>;
    fn repeated(count: usize, val: T) where T: Clone -> Vec<T>;
    fn repeated_with(count: usize, val: impl FnMut() -> T) -> Vec<T>;
}

You ask and you shall receive:

Although the last one is being deprecated for

v.resize_with(size, Default::default);

On a side note, all of these functions require that they be used on a preexisting object, so therefore your code would become:

let mut v = Vec::with_capacity(count);
v.resize_with(count, || None::<T>);
3 Likes

I don't think that blog works for you, array is different from vector.

  1. array use Copy to repeat element, and vector use Clone.
  2. element stored in stack, but in heap in vector

you should implement Clone trait for ZSocket, or manually init it as @krdln did.

I thought that initializing an array and then turning that into a vec would be an alternative that is not too costly.

Don't know how to finish this correctly.
How about something like?:

macro_rules! myvec {
    (Option<$t:ty>::None; $n:expr) => (
        // insert missing efficient code here
    );
    ($elem:expr; $n:expr) => (
        $crate::vec::from_elem($elem, $n)
    );
    ($($x:expr),*) => (
        <[_]>::into_vec(box [$($x),*])
    );
    ($($x:expr,)*) => ($crate::vec![$($x),*])
}

array is a block of memory on the stack that is described by a 2-component descriptor (usize, usize) consisting of non-zero start address and array length (in elements). vec is a block of memory in the heap that is described by a 3-component descriptor (usize, usize, usize) consisting of non-zero start address, length (in elements), and capacity (in elements).

I'd say either

pub fn vec_of_none<T>(n: usize) -> Vec<Option<T>> {
    let mut v = Vec::new();
    v.resize_with(n, || None);
    v
}

or

pub fn vec_of_none<T>(n: usize) -> Vec<Option<T>> {
    std::iter::repeat_with(|| None).take(n).collect()
}
2 Likes

Thanks. I thought that creating an array using the methods discussed in Josh's post and then turning it into a Vec would be clear code and not have too bad performance

This compiles:

macro_rules! myvec {
    (None; $n:expr) => (
        std::iter::repeat_with(|| None).take($n).collect()
    );
    ($elem:expr; $n:expr) => (
        std::vec::from_elem($elem, $n)
    );
    ($($x:expr),*) => (
        <[_]>::into_vec(box [$($x),*])
    );
    ($($x:expr,)*) => (vec![$($x),*])
}```

Usage `let sockets: Vec<Option<ZSocket>> = myvec![None; nodes.len()];`

Please comment

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.