How could I create an array of structs containing big arrays with a fixed size? I want to use arrays and not vectors.
This code is an example but doesn't compile
struct _Tmove {
data1: usize,
data2: u64,
data3: bool,
}
struct _TmoveP {
data4: Box<[_Tmove]>,
data5: isize,
}
fn main() {
let mut gen_list = Box::new([
_TmoveP {
data5: 1,
data4: Box::new([_Tmove { data1: 5, data2: 1, data3: true }; 300]),
}
; 100000]);
assert!(gen_list[0].data4[0].data1==5);
}
error[E0277]: the trait bound `_Tmove: std::marker::Copy` is not satisfied
--> src/main.rs:16:29
|
16 | data4: Box::new([_Tmove { data1: 5, data2: 1, data3: true }; 300]),
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: the `Copy` trait is required because the repeated element will be copied
error[E0277]: the trait bound `_TmoveP: std::marker::Copy` is not satisfied
--> src/main.rs:13:33
|
13 | let mut gen_list = Box::new([
| ^
|
= note: the `Copy` trait is required because the repeated element will be copied
I am using Rust 1.12.
In order to benefit from the initialization syntax: [expr; N]
, the result of expr
need to be Copy
(as copies need be made).
#[derive(Copy, Clone)]
struct _Tmove {
data1: usize,
data2: u64,
data3: bool,
}
#[derive(Copy, Clone)]
struct _TmoveP {
data4: Box<[_Tmove]>,
data5: isize,
}
However, in this case, _TmoveP
cannot be Copy
because it contains a Box
which is not Copy
.
Alright, let's get rid of Box
:
#[derive(Copy, Clone)]
struct _TmoveP {
data4: [_Tmove; 300],
data5: isize,
}
Sounds great?
But unfortunately, [_Tmove; 300]
is not Clone
either :( We are unfortunately hitting a limitation of the Rust compiler (it works for size less than 32).
The Copy
is easy enough... but first we have to implement Clone
manually. The naive way is no fun, but it's easy enough:
impl Clone for _TmoveP {
fn clone(&self) -> _TmoveP {
unsafe {
let mut res = _TmoveP {
data4: std::mem::uninitialized(),
data5: self.data5,
};
std::ptr::copy_nonoverlapping(
&self.data4 as *const _Tmove,
std::mem::transmute(&mut res.data4),
300
);
res
}
}
}
Note: for some reason &mut res.data4 as *mut _
would not compile... whatever :x
However, @Francis Gagné reminded me in the comments, there is a weird trick with Copy
types:
impl Clone for _TmoveP {
fn clone(&self) -> _TmoveP { *self }
}
This works, for some reason, and is handy in those situations.
And, finally, this works... oh wait, there's an issue in main
!
fn main() {
let gen_list = Box::new([
_TmoveP {
data5: 1,
data4: [_Tmove { data1: 5, data2: 1, data3: true }; 300],
}
; 100000]);
assert!(gen_list[0].data4[0].data1==5);
}
Alright, here we go.
What's the deal with arrays only really working for sizes smaller than 32?
Simply put: Rust does not have (yet?) support for non-type generic parameters.
Arrays are special-cased, to an extent, but require implementing traits for each size independently... so the standard library implements its traits for arrays up to size 32, because it seemed a good trade-off.
You are receiving this error because you're trying to initialise an array with the default initialisation syntax, but your struct does not implement the Copy
trait, so this is not allowed. You can see the reasons here, but, in a nutshell, the default initialisation syntax will create one copy of your struct, then try to copy it 100,000 times. Obviously, this isn't allowed if your struct isn't marked Copy
, so an error is raised.
Normally, this could be solved by marking both your structs as Copy
, like so:
#[derive(Clone, Copy)]
struct _Tmove {
data1: usize,
data2: u64,
data3: bool,
}
#[derive(Clone, Copy)]
struct _TmoveP {
data4: Box<[_Tmove]>,
data5: isize,
}
However, you'll notice that this still doesn't compile, because you've not actually got an array here. You've actually used the type for a slice (take a look at this similar issue). Slices don't implement Copy
, so your code cannot compile because the _TmoveP
struct can only derive Copy
if all its fields are Copy
.
It's unclear whether the array will always have a fixed size. If it will, then you need to use the type [T; N]
, where T
is your type and N
is the number of elements (e.g. [i32; 300]
). If not, you need a Vec<T>
.
If you use an array, you hit yet another problem. Arrays implement Copy
(up to 32), but not Clone
, and we need to implement Clone
for _TmoveP
before we can implement Copy
. So, let's do it ourselves:
impl Clone for _TmoveP {
fn clone(&self) -> _TmoveP {
_TmoveP {
data4: self.data4,
data5: self.data5
}
}
}
You can then remove the #[derive(Clone)]
from _TmoveP
(just leaving Copy
), and we finally reach a working solution! Here is the playground link for my solution.