Array of non-Copy

I have a world object that I want to contain a list of TCP writers (tokio WriteHalf). I can express this in a struct, but I seem to be unable to create/initialize it.

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=fdca1aaceea663b170e814aa85532fa6

I think I understand what the issue is: tokio doesn't want people to Copy the WriteHalf, which I understand. And rust tried to initialize the array it looks at the Some() type and concludes that it doesn't have Copy so it fails, even though I explicitly want to initialize it as an array of None.

Am I understanding the problem correctly? What would be a good way to work around this problem? I assume those using ECS for TCP connections have encountered this in some way or another. :slight_smile:

Looks like there's a crate for this that provides a macro to create an array of non-Copy type:

array![None; 32]
1 Like

The situation here will probably get better as const generics get stabilized.

In the mean time there are a bunch of crates that can help. To add to the one already mentioned, https://crates.io/crates/arraytools allows things like

<[_; 32]>::generate(|| None)
1 Like

There is another solution but using unsafe here.

1 Like

AFAIK MaybeUninit::uninit().assume_init() is always insta-UB.

I don't understand, what about this? Not safe?

In this case it's okay because "all bytes uninitialized" is a valid value of type

[MaybeUninit::<Option<WriteHalf<TcpStream>>>; 32]
3 Likes

Since doing that unsafe {} is error-prone, there is also the https://docs.rs/array-init crate that has factored that out.


Indeed, MaybeUninit::uninit().assume_init(), i.e., mem::uninitialized(), does not break validity invariants when used on [MaybeUninit<T>; N] types, as well as with inhabited ZSTs (although this case can break safety invariants associated with the ZST API, such as it being with singleton properties). Any other case breaks the validity invariant and is thus "instant UB".

2 Likes

Since nobody has mentioned it yet: You can write [None, None, None, None, None, None] to initialize an array like this without any unsafe code or macros. (Yes, it gets quite verbose when you get up to 32-element arrays.)

The real code has 16384 entries. :slight_smile: (I should probably have mentioned this in the original post).

That doesn't sound like something that fits on the stack. I know you're moving it into the Arc, but it's still created on the stack initially, and you would need something like Arc::new_uninit to get around that.

1 Like

That's big enough that I'd suggest using vec![None; 1 << 14].into_boxed_slice() instead of the array...

3 Likes